【问题标题】:Should I explicitely scope variables?我应该明确范围变量吗?
【发布时间】:2013-04-22 05:00:53
【问题描述】:

这更像是一个风格问题,因为我知道在实践中大多数编译器可能会优化以产生相同的效果,但我一直在阅读,一般来说,您应该始终在它们的范围内声明/定义变量被使用。因此,在我无法内联声明的情况下,例如下面的 sn-p,我考虑过将索引变量括在范围括号(大括号,在这种情况下不确定你如何称呼它们)中,以便明确限制这些变量的范围。这是好习惯吗?如果有,请解释一下原因吗?

{
    size_t i = 0; // this variable has no use outside of the range-based for loop
    for (auto const input : input_vector)
    {
        neuron_sequence[i].ForceSignal(input);
        ++i;
    }
}

【问题讨论】:

  • "花括号,在这种情况下不知道你怎么称呼它们" 它们被称为“花括号”。在所有情况下;)
  • “大多数编译器可能会优化以提供相同的效果”。你能举个例子吗?
  • “大多数编译器可能会优化以提供相同的效果” - 在 3.7.3/3 中通常不可能:“如果具有自动存储持续时间的变量具有初始化或具有副作用的析构函数,它不应在其块结束之前被销毁,即使它看起来未使用也不应作为优化而被消除,除非类对象或其复制/移动可以被消除为在 12.8 中指定。”
  • @NicolBolas:标准只是称它们为大括号......卷曲是多余的。
  • 感谢您的回复。都非常有帮助。

标签: c++ scope


【解决方案1】:

当然这是一个好习惯。它明确限制了可以使用该变量的位置。我经常这样做。像这样的作用域也用于强制一些对象析构函数运行。

例如:

std::vector<int> v;
v.resize( 10 ); // now holds memory for 10 ints

你如何清理这个内存?没有可调用的函数或任何手动告诉向量v 清理其内存的方式。一个解决方案是强制它超出范围(假设我正确使用了交换):

std::vector<int> v;
v.resize( 10 ); // now holds memory for 10 ints
{
  std::vector<int> temp;
  temp.swap( v );
} // temp goes out of scope and clears the memory that v used to hold

另一个常见的用法是在 switch case 中。很多时候我需要在 switch 中创建一个临时变量:

switch( val )
{
case constant:
  {
    int x = 10;
    // ... do stuff
  }
}

我能记住的最后一个地方是为某种代码编写测试用例时。经常进行单元测试时,我只想尽可能快地编写测试代码,而不需要花费太多的开发时间。因此,我将一堆相关测试放在一个函数中,但将局部变量包装在不同的范围内,以确保我不会遇到任何奇怪的错误(可能通过测试共享迭代器)。

【讨论】:

  • 整体+1,只是对整个“手动告诉向量v清理/交换”的评论:不需要额外的变量和范围,只是做std::vector&lt;int&gt;().swap(v); 这是一个常见的成语。
  • @syam 我明白了,是的,这行得通。但是,您不确定何时右值向量将超出范围并销毁。有了额外的范围,您可以确定确切的时间。
  • "你不确定..." => 是的,你知道:向量是临时的,因为没有变量可以“保持”它,所以它立即被销毁。它与您编写的代码完全相同相同,只是不那么冗长。 ;)
  • @syam Ah 刚查了一下,看起来破坏发生在创建右值的表达式的末尾,所以你是对的!
【解决方案2】:

是的,你应该明确地作用域变量:

  1. 作用域定义了局部变量的生命周期,因此适当地定义变量的范围意味着变量只有在它们满足其所需用途时才处于活动状态,而不仅仅是处于活动状态并占用内存。
  2. 在相同命名变量的情况下,局部变量隐藏或隐藏相同命名的全局变量。因此,明确范围可以提高读者的可读性。(至少我是这么认为的

【讨论】:

  • 主要的缺点是过度使用额外的作用域会导致代码混乱并降低可读性。这是一个权衡。
  • @Antimony:这是感知问题而不是技术问题。从技术上讲,明确范围是建议。
  • @AlokSave:问题开始于“这更像是一个风格问题......” - 给出一个需要事后评论的答案,说“我只考虑技术问题”过于狭窄.
  • @TonyD:我认为这不仅仅是一个风格问题。如果 OP 认为这是他/她对手头的事情的误解或缺乏理解,我不认为是这种情况,因为 Q 也说 “这是一种好的做法吗?如果是这样,你能解释一下原因吗? ".我通常避免回答只是说,使用这个,因为我觉得这个我宁愿回答一个有技术基础的 Q。我为我的回答提供了技术基础,而不是沉迷于非标准的基于感知的谈话。
  • @Antimony 如果您发现显式范围界定会使您的代码变得混乱,那么您当前的范围(例如函数)可能不止一件事,并且您可能希望将其重构为更小的函数。局部作用域距离功能重构仅一步之遥,因此如果有的话,它应该可以减少混乱。
【解决方案3】:

对于像整数这样的小数据类型,你真的不需要担心,因为正如你所说,编译器会根据变量的 liveness 以及它是否 来优化代码>reaches 某个位置。在这种情况下,它更多的是风格问题。而且我建议不要经常这样做,因为代码的可读性和易于维护也与性能一样重要。

但是,对于复杂类型,像这样限制生命周期可能很有用。例如,对于内部分配大量内存的向量,如果其范围受到限制,则可以节省一些空间。

【讨论】:

  • 类型无关紧要。这关乎生命周期和可见性。
  • @CaptainObvlious 好吧,这取决于,因为对于涉及指针和动态分配内存的类型,编译器很难有效地分析活跃度并达到定义。如果可以的话,你可以帮忙。
  • @CaptainObvlious:“类型无关紧要。重要的是生命周期和可见性。” - 类型确实很重要:3.7.3/3:“如果具有自动存储持续时间的变量具有初始化或具有副作用的析构函数,则不应在其块结束之前将其销毁,也不应作为优化消除,即使它似乎没有被使用,除了一个类对象或其复制/移动可以按照 12.8 中的规定被消除。”
【解决方案4】:
  • 当您发现自己担心对象的生命周期时(通常是因为使用的内存/资源,或者希望防止意外或促进故意重用相同的标识符),紧密的作用域是好的
    • 例如做一些重复的操作 - 甚至可能使用宏替换 - 需要一个临时的但更改名称没有特殊目的并且很乏味
  • 您不能总是这样做:您会遇到很多情况,其中适合一个变量的最紧凑范围与另一个变量的范围重叠:例如试图在{ X a = 1; X b(a, 2); ++a; } ++b; 中“限定”a 会过早地破坏b
  • 在某些情况下,创建大量小范围会使源代码显着膨胀,从而更难以直观地理解和维护。检查引入的范围是否没有控制 if/for/while 语句需要花费一些脑力,而且很难一目了然地看到整个函数流程。当然,减少函数范围内的变量数量也可以减少脑力劳动 - 所以这是一种平衡行为。

总的来说 - 有选择性是很好的,但你会感觉到什么时候有必要。如果不确定,可能没关系。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-11
    • 2011-09-02
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    相关资源
    最近更新 更多