14

有时您会遇到带有额外大括号的代码,这与范围无关,只是为了可读性和避免错误。

例如:

GetMutexLock( handle ) ; 
{
  // brace brackets "scope" the lock,
  // must close block / remember
  // to release the handle.
  // similar to C#'s lock construct
}
ReleaseMutexLock( handle ) ;

我见过的其他地方是:

glBegin( GL_TRIANGLES ) ;
{
  glVertex3d( .. ) ;
  glVertex3d( .. ) ;
  glVertex3d( .. ) ;
} // must remember to glEnd!
glEnd() ; 

如果互斥锁没有被释放,这会引入编译器错误(假设您同时记住了 } 和Release()调用)。

  1. 这是一个不好的做法吗?为什么?
  2. 如果不是,它会改变代码的编译方式或使其变慢吗?
4

8 回答 8

34

大括号本身很好,它们所做的只是限制范围,你不会减慢任何速度。它可以被视为更清洁。(总是更喜欢干净的代码而不是快速的代码,如果它更干净,在你分析之前不要担心速度。)


但就资源而言,这是不好的做法,因为您已将自己置于泄漏资源的位置。如果块中的任何东西抛出或返回,那么你就死定了。

使用 Scope-bound Resource Management (SBRM,也称为 RAII),通过使用析构函数将资源限制在一个范围内:

class mutex_lock
{
public:
    mutex_lock(HANDLE pHandle) :
    mHandle(pHandle)
    {
        //acquire resource
        GetMutexLock(mHandle);
    }

    ~mutex_lock()
    {
        // release resource, bound to scope
        ReleaseMutexLock(mHandle);
    }

private:
    // resource
    HANDLE mHandle;

    // noncopyable
    mutex_lock(const mutex_lock&);
    mutex_lock& operator=(const mutex_lock&);
};

所以你得到:

{
  mutex_lock m(handle);
  // brace brackets "scope" the lock,
  // AUTOMATICALLY
}

这样做会占用所有资源,它更清洁、更安全。如果你可以说“我需要释放这个资源”,那你就做错了;它们应该被自动处理。

于 2010-08-25T17:20:33.497 回答
18

大括号影响变量范围。据我所知,这就是他们所做的一切。

是的,这会影响程序的编译方式。析构函数将在块结束时调用,而不是等到函数结束。

这通常是您想要做的。例如,您的 GetMutexLock 和 ReleaseMutexLock 将是更好的 C++ 代码,如下所示:

struct MutexLocker {
  Handle handle;
  MutexLocker(handle) : handle(handle) { GetMutexLock(handle); }
  ~MutexLocker() { ReleaseMutexLock(handle); }    
};
...
{
  MutexLocker lock(handle);
  // brace brackets "scope" the lock,
  // must close block / remember
  // to release the handle.
  // similar to C#'s lock construct
}

使用这种更 C++ 的风格,锁会在块结束时自动释放。它将在所有情况下发布,包括异常,除了 setjmp/longjmp 或程序崩溃或中止。

于 2010-08-25T17:20:10.153 回答
3

这不是坏习惯。它不会使任何事情变慢;这只是一种结构化代码的方式。

让编译器为您进行错误检查和强制执行总是一件好事!

于 2010-08-25T17:15:19.500 回答
3

原始示例中的特定位置{ ... }纯粹用作格式化糖,使一组逻辑相关语句的开始位置和结束位置更加明显。如您的示例所示,它对编译的代码没有影响。

我不知道“如果未释放互斥锁,这会引入编译器错误”是什么意思。这根本不是真的。这种使用{ ... }不能也不会引入任何编译器错误。

Whether it is a good practice is a matter of personal preference. It looks OK. Alternatively, you can use comments and/or indentation to indicate logical grouping of statements in the code, without any extra { ... }.

There are various scoping-based techniques out there, some of which have been illustrated by the other answers here, but what you have in your OP doesn't even remotely look anything like that. Once again, what you have in your OP (as shown) is purely a source formatting habit with superfluous { ... } that have no effect on the generated code.

于 2010-08-25T17:32:33.403 回答
2

除了在该块的末尾而不是周围块的末尾调用任何析构函数之外,它对编译的代码没有任何影响,除非编译器完全疯了。

就个人而言,我认为这是不好的做法。避免您在这里可能犯的错误的方法是使用范围资源管理(有时称为 RAII),而不是使用容易出错的印刷提醒。我会把代码写成类似

{
    mutex::scoped_lock lock(mutex);
    // brace brackets *really* scope the lock
}   // scoped_lock destructor releases the lock

{
    gl_group gl(GL_TRIANGLES); // calls glBegin()
    gl.Vertex3d( .. );
    gl.Vertex3d( .. );
    gl.Vertex3d( .. );
} // gl_group destructor calls glEnd()
于 2010-08-25T17:21:48.313 回答
2

如果您将代码放入大括号中,您可能应该将其分解为自己的方法。如果它是一个单独的离散单元,为什么不给它贴上标签并在功能上分解它呢?这将明确该块的作用,以后阅读代码的人不必弄清楚。

于 2010-08-25T17:26:07.387 回答
1

任何提高可读性恕我直言的做法都是很好的做法。如果添加大括号有助于提高可读性,那就去吧!

添加额外的大括号不会改变代码的编译方式。它不会使程序的运行变慢。

于 2010-08-25T17:17:18.527 回答
1

这在带有对象析构函数的 C++ 中更有用(恕我直言);你的例子在 C 中。

想象一下,如果你创建了一个 MutexLock 类:

class MutexLock {
private:
    HANDLE handle;
public:
    MutexLock() : handle(0) {
        GetMutexLock(handle);
    }

    ~MutexLock() {
        ReleaseMutexLock(handle);
    }
}

然后,您可以通过提供带有大括号的新范围来将该锁定范围限定为需要它的代码:

{
    MutexLock mtx;  // Allocated on the stack in this new scope

    // Use shared resource
}
// When this scope exits the destructor on mtx is called and the stack is popped
于 2010-08-25T17:22:22.380 回答