C++ 中不必要的花括号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9704083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:11:50  来源:igfitidea点击:

Unnecessary curly braces in C++?

c++code-formatting

提问by iikkoo

When doing a code review for a colleague today I saw a peculiar thing. He had surrounded his new code with curly braces like this:

今天在为同事做代码时,看到了一个奇特的东西。他用这样的花括号包围了他的新代码:

Constructor::Constructor()
{
   existing code

   {
      New code: do some new fancy stuff here
   }

   existing code
}

What is the outcome, if any, from this? What could be the reason for doing this? Where does this habit come from?

如果有的话,结果是什么?这样做的原因是什么?这个习惯从何而来?

Edit:

编辑:

Based on the input and some questions below I feel that I have to add some to the question, even though that I already marked an answer.

根据输入和下面的一些问题,我觉得我必须在问题中添加一些内容,即使我已经标记了答案。

The environment is embedded devices. There is a lot of legacy C code wrapped in C++ clothing. There are a lot of C turned C++ developers.

环境是嵌入式设备。有很多遗留的 C 代码包裹在 C++ 衣服中。有很多从 C 转为 C++ 的开发人员。

There are no critical sections in this part of the code. I have only seen it in this part of the code. There are no major memory allocations done, just some flags that are set, and some bit twiddling.

这部分代码没有临界区。我只在这部分代码中看到过。没有完成主要的内存分配,只是设置了一些标志,还有一些摆弄。

The code that is surrounded by curly braces is something like:

被花括号包围的代码是这样的:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

(Don't mind the code, just stick to the curly braces... ;) ) After the curly braces there are some more bit twiddling, state checking, and basic signaling.

(不要介意代码,只要坚持使用大括号... ;) ) 在大括号之后还有一些小动作、状态检查和基本信号。

I talked to the guy and his motivation was to limit the scope of variables, naming clashes, and some other that I couldn't really pick up.

我和那个人谈过,他的动机是限制变量的范围,命名冲突,以及其他一些我无法真正理解的东西。

From my POV this seems rather strange and I don't think that the curly braces should be in our code. I saw some good examples in all the answers on why one could surround code with curly braces, but shouldn't you separate the code into methods instead?

从我的 POV 看来,这似乎很奇怪,我认为花括号不应该出现在我们的代码中。我在所有答案中都看到了一些很好的例子,说明为什么可以用花括号将代码括起来,但您不应该将代码分成方法吗?

回答by unwind

It's sometimes nice since it gives you a new scope, where you can more "cleanly" declare new (automatic) variables.

它有时很好,因为它为您提供了一个新的范围,您可以在其中更“干净地”声明新的(自动)变量。

In C++this is maybe not so important since you can introduce new variables anywhere, but perhaps the habit is from C, where you could not do this until C99. :)

C++这也许不那么重要,因为你可以在任何地方引入新的变量,但也许是习惯是C,你不能这样做,直到C99。:)

Since C++has destructors, it can also be handy to have resources (files, mutexes, whatever) automatically released as the scope exits, which can make things cleaner. This means you can hold on to some shared resource for a shorter duration than you would if you grabbed it at the start of the method.

由于C++具有析构函数,因此在作用域退出时自动释放资源(文件、互斥体等)也很方便,这可以使事情变得更清晰。这意味着与在方法开始时获取共享资源相比,您可以在更短的时间内保留某些共享资源。

回答by ruakh

One possible purpose is to control variable scope. And since variables with automatic storage are destroyed when they go out of scope, this can also enable a destructor to be called earlier than it otherwise would.

一种可能的目的是控制变量范围。并且由于具有自动存储的变量在超出范围时会被销毁,因此这也可以使析构函数比其他情况更早地被调用。

回答by Nawaz

The extra braces are used to define the scope of the variable declared inside the braces. It is done so that the destructor will be called when the variable goes out of scope. In the destructor, you may release a mutex (or any other resource) so that other could acquire it.

额外的大括号用于定义在大括号内声明的变量的范围。这样做是为了在变量超出范围时调用析构函数。在析构函数中,您可以释放互斥锁(或任何其他资源),以便其他人可以获取它。

In my production code, I've written something like this :

在我的生产代码中,我写了这样的东西:

void f()
{
   //some code - MULTIPLE threads can execute this code at the same time

   {
       scoped_lock lock(mutex); //critical section starts here

       //critical section code
       //EXACTLY ONE thread can execute this code at a time

   } //mutex is automatically released here

  //other code  - MULTIPLE threads can execute this code at the same time
}

As you can see, in this way, you can use scoped_lockin a function and at the same time, can define its scope by using extra braces. This makes sure that even though the code outside the extra braces can be executed by multiplethreads simultaneously, the code inside the braces will be executed by exactly one threadat a time.

如您所见,通过这种方式,您可以scoped_lock在函数中使用,同时可以通过使用额外的大括号来定义其作用域。这确保即使额外大括号外的代码可以由多个线程同时执行,大括号内的代码一次只能由一个线程执行。

回答by Ira Baxter

As others have pointed out, a new block introduces a new scope, enabling one to write a bit of code with its own variables that don't trash the namespace of the surrounding code, and doesn't use resources any longer than necessary.

正如其他人指出的那样,一个新块引入了一个新的作用域,使人们能够使用自己的变量编写一些代码,这些代码不会破坏周围代码的命名空间,并且不会使用不必要的资源。

However, there's another fine reason for doing this.

然而,这样做还有另一个很好的理由。

It is simply to isolate a block of code that achieves a particular (sub)purpose. It is rare that a single statement achieves a computational effect I want; usually it takes several. Placing those in a block (with a comment) allows me tell the reader (often myself at a later date):

它只是隔离实现特定(子)目的的代码块。很少有单个语句达到我想要的计算效果;通常需要几个。将它们放在一个块中(带有注释)允许我告诉读者(通常是我自己在以后的日期):

  • This chunk has a coherent conceptual purpose
  • Here's all the code needed
  • And here's a comment about the chunk.
  • 这个块有一个连贯的概念目的
  • 这是所有需要的代码
  • 这是关于块的评论。

e.g.

例如

{  // update the moving average
   i= (i+1) mod ARRAYSIZE;
   sum = sum - A[i];
   A[i] = new_value;
   sum = sum + new_value;
   average = sum / ARRAYSIZE ;  
}

You might argue I should write a function to do all that. If I only do it once, writing a function just adds additional syntax and parameters; there seems little point. Just think of this as a parameterless, anonymous function.

您可能会争辩说我应该编写一个函数来完成所有这些工作。如果我只做一次,写一个函数只会增加额外的语法和参数;似乎没什么意义。只需将其视为无参数的匿名函数即可。

If you are lucky, your editor will have a fold/unfold function that will even let you hide the block.

如果幸运的话,您的编辑器将具有折叠/展开功能,甚至可以让您隐藏块。

I do this all the time. It is great pleasure to know the bounds of the code I need to inspect, and even better to know that if that chunk isn't the one I want, I don't have to look at any of the lines.

我一直这样做。很高兴知道我需要检查的代码的边界,甚至更好的是知道如果那个块不是我想要的,我不必查看任何行。

回答by arne

One reason could be that the lifetime of any variables declared inside the new curly braces block is restricted to this block. Another reason that comes to mind is to be able to use code folding in the favourite editor.

一个原因可能是在新的花括号块中声明的任何变量的生命周期都限于此块。想到的另一个原因是能够在最喜欢的编辑器中使用代码折叠。

回答by Branko Dimitrijevic

This is the same as an if(or whileetc..) block, just withoutif. In other words, you introduce a scope without introducing a control structure.

这与if(或while等)块相同,只是没有if. 换句话说,您在不引入控制结构的情况下引入了作用域。

This "explicit scoping" is typically useful in following cases:

这种“显式范围”在以下情况下通常很有用:

  1. To avoid name clashes.
  2. To scope using.
  3. To control when the destructors are called.
  1. 避免名称冲突。
  2. 范围using
  3. 控制何时调用析构函数。

Example 1:

示例 1:

{
    auto my_variable = ... ;
    // ...
}

// ...

{
    auto my_variable = ... ;
    // ...
}

If my_variablehappens to be a particularly good namefor two different variables that are used in isolation from each other, then explicit scoping allows you to avoid inventing a new name just to avoid the name clash.

如果my_variable恰好是两个彼此隔离使用的不同变量的特别好的名称,那么显式作用域允许您避免为了避免名称冲突而发明新名称。

This also allows you to avoid using my_variableout of its intended scope by accident.

这也允许您避免my_variable意外使用超出其预期范围。

Example 2:

示例 2:

namespace N1 { class A { }; }
namespace N2 { class A { }; }

void foo() {

    {
        using namespace N1;
        A a; // N1::A.
        // ...
    }

    {
        using namespace N2;
        A a; // N2::A.
        // ...
    }

}

Practical situations when this is useful are rare and may indicate the code is ripe for refactoring, but the mechanism is there should you ever genuinely need it.

这种有用的实际情况很少见,可能表明代码重构的时机已经成熟,但是如果您真的需要它,则该机制就在那里。

Example 3:

示例 3:

{
    MyRaiiClass guard1 = ...;

    // ...

    {
        MyRaiiClass guard2 = ...;
        // ...
    } // ~MyRaiiClass for guard2 called.

    // ...

} // ~MyRaiiClass for guard1 called.

This can be important for RAIIin cases when the need for freeing resources does not naturally "fall" onto boundaries of functions or control structures.

当释放资源的需求不会自然地“落在”功能或控制结构的边界上时,这对于RAII可能很重要。

回答by learnvst

This is really useful when using scoped locks in conjunction with critical sections in multithreaded programming. Your scoped lock initialised in the curly braces (usually the first command) will go out of scope at the end of the end of the block and so other threads will be able to run again.

这在多线程编程中将作用域锁与临界区结合使用时非常有用。您在花括号中初始化的作用域锁(通常是第一个命令)将在块的末尾超出作用域,因此其他线程将能够再次运行。

回答by Useless

Everyone else already covered correctly the scoping, RAII etc. possiblities, but since you mention an embedded environment, there is one further potential reason:

其他人都已经正确地涵盖了范围、RAII 等可能性,但是既然您提到了嵌入式环境,那么还有一个潜在的原因:

Maybe the developer doesn't trust this compiler's register allocation or wants to explicitly control the stack frame size by limiting the number of automatic variables in scope at once.

也许开发人员不信任这个编译器的寄存器分配,或者希望通过一次限制范围内自动变量的数量来显式控制堆栈帧大小。

Here isInitwill likely be on the stack:

这里isInit可能会在堆栈上:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

If you take out the curly braces, space for isInitmay be reserved in the stack frame even after it could potentially be reused: if there are lots of automatic variables with similarly localized scope, and your stack size is limited, that could be a problem.

如果去掉花括号,isInit即使在它可能被重用之后,堆栈帧中也可能保留空间:如果有很多具有类似本地化范围的自动变量,并且您的堆栈大小有限,那可能是一个问题。

Similarly, if your variable is allocated to a register, going out of scope should provide a strong hint that register is now available for re-use. You'd have to look at the assembler generated with and without the braces to figure out if this makes a real difference (and profile it - or watch for stack overflow - to see if this difference really matters).

类似地,如果您的变量被分配给一个寄存器,超出范围应该提供一个强有力的提示,即现在可以重新使用寄存器。您必须查看使用和不使用大括号生成的汇编程序,以确定这是否有真正的区别(并对其进行分析 - 或观察堆栈溢出 - 以查看这种差异是否真的很重要)。

回答by kertronux

I think others have covered scoping already, so I'll mention the unnecessary braces might also serve purpose in the development process. For example, suppose you are working on an optimization to an existing function. Toggling the optimization or tracing a bug to a particular sequence of statements is simple for the programmer -- see the comment prior to the braces:

我认为其他人已经涵盖了范围界定,所以我会提到不必要的大括号也可能在开发过程中发挥作用。例如,假设您正在对现有函数进行优化。切换优化或将错误跟踪到特定的语句序列对程序员来说很简单——请参阅大括号前的注释:

// if (false) or if (0) 
{
   //experimental optimization  
}

This practice is useful in certain contexts like debugging, embedded devices, or personal code.

这种做法在某些情况下很有用,例如调试、嵌入式设备或个人代码。

回答by Cloud

I agree with "ruakh". If you want a good explanation of the various levels of scope in C, check out this post:

我同意“ruakh”。如果您想对 C 中的各个级别的范围有一个很好的解释,请查看这篇文章:

Various Levels of Scope in C Application

C 应用程序中不同级别的范围

In general, the use of "Block scope" is helpful if you want to just use a temporary variable that you don't have to keep track of for the lifetime of the function call. Additionally, some people use it so you can use the same variable name in multiple locations for convenience, though that's not generally a good idea. eg:

一般来说,如果您只想使用在函数调用的生命周期内不必跟踪的临时变量,则使用“块范围”会很有帮助。此外,有些人使用它以便您可以在多个位置使用相同的变量名以方便起见,尽管这通常不是一个好主意。例如:

int unusedInt = 1;

int main(void) {
  int k;

  for(k = 0; k<10; k++) {
    int returnValue = myFunction(k);
    printf("returnValue (int) is: %d (k=%d)",returnValue,k);
  }

  for(k = 0; k<100; k++) {
    char returnValue = myCharacterFunction(k);
    printf("returnValue (char) is: %c  (k=%d)",returnValue,k);
  }

  return 0;
}

In this particular example, I have defined returnValue twice, but since it is just at block scope, instead of function scope (ie: function scope would be, for example, declaring returnValue just after int main(void) ), I don't get any compiler errors, as each block is oblivious to the temporary instance of returnValue declared.

在这个特定的例子中,我已经定义了 returnValue 两次,但由于它只是在块范围内,而不是函数范围(即:函数范围将是,例如,在 int main(void) 之后声明 returnValue ),我不获取任何编译器错误,因为每个块都没有注意到声明的 returnValue 的临时实例。

I can't say that this is a good idea in general (ie: you probably shouldn't re-use variable names repeatedly from block-to-block), but in general, it saves time and lets you avoid having to manage the value of returnValue across the entire function.

我不能说这是一个好主意(即:您可能不应该从一个块到另一个块重复使用变量名),但总的来说,它可以节省时间并让您不必管理整个函数的 returnValue 值。

Finally, please note the scope of the variables used in my code sample:

最后,请注意我的代码示例中使用的变量的范围:

int:  unusedInt:   File and global scope (if this were a static int, it would only be file scope)
int:  k:           Function scope
int:  returnValue: Block scope
char: returnValue: Block scope