2010-08-25 108 views
12

有时您会遇到带有额外大括号的代码,这些代码与范围无关,仅用于可读性和避免错误。C++代码中的额外大括号

例如:我已经看到了

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. 如果不是这样,它是否可以改变代码的编译方式或使其变慢?
+2

在OpenGL中,此练习旨在增强代码的可读性。 glBegin()调用中的方括号作为应该绘制的顶点组的视觉分隔符。 – karlphillip 2010-08-25 17:31:36

+0

这是一个可怕的做法!为什么有人会使用块分隔符来让代码块脱颖而出呢?我可以想象他们错过的所有微观优化...所有人都为可读性而牺牲的东西。什么是世界来? – 2010-08-25 17:47:46

+1

@Noah,我把你改名为Sarcastic Cat。 – bobobobo 2010-08-25 18:14:21

回答

30

大括号本身很好,他们所做的只是限制范围,你不会放慢速度。它可以被视为更清洁。 (总是喜欢干净的代码了快速的代码,如果它是清洁,不用担心,直到你个人资料的速度。)


但是相对于资源它是不好的做法,因为你已经把自己放在一个位置泄漏资源。如果块中的任何内容抛出或返回,那么你已经死了。

使用范围限制的资源管理(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 
} 

这样做是否会全部资源,它更干净,更安全。如果你有能力说“我需要释放这个资源”,那么你做错了;他们应该自动处理。

+7

+1正确性......以及我不知道的这个新的SBRM首字母缩略词!就表达意图而言,要比RAII好得多。甚至似乎还没有一个维基百科页面! – 2010-08-25 17:47:55

3

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

让编译器做错误检查&为你执行永远是件好事!

+0

+1让编译器做你的工作... – 2010-08-25 17:19:48

+2

不幸的是你自己释放资源是容易出错的... – 2010-08-25 17:50:19

1

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

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

+0

{I {think {that {adding {braces {can {reduce} readability}}}}}} 。说真的,大括号告诉我“这里有一个控制结构”。如果没有,我会一直寻找它。让我花费认知和一秒钟的时间去寻找不存在的东西,这是不可读的。 – 2010-08-25 17:27:36

+0

@David - 我不认为OP在考虑在代码中添加任意数量的大括号。我指的是OP使用大括号的方式。显然,在你的例子中添加大括号并不能提高可读性。 – Starkey 2010-08-25 17:41:45

+0

就我而言,使用大括号除了指示控件结构之外的任何用途都不利于可读性。这不算作控制结构。使用它们来限制RAII分配资源的范围。 – 2010-08-25 17:53:38

17

大括号影响变量作用域。据我所知,这是他们所做的一切。

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

通常这是你想要做的。例如,您的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或程序崩溃或中止外。

+0

这是正确的路要走。 – Puppy 2010-08-25 17:26:12

+12

你需要给储物柜命名,或者它只是一个临时消失的临时物品。你应该改变它后的评论来匹配新的资源管理。 – GManNickG 2010-08-25 17:28:34

+0

正确的语法是'MutexLocker句柄;',而不是'MutexLocker(句柄);'。 – kennytm 2010-08-25 18:29:40

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() 
1

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

试想一下,如果你犯了一个MutexLock类:

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

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

,那么你可以作用域锁定只是需要它通过提供一个新的领域与括号中的代码:

1

如果你把代码放在大括号中,你应该把它分解成它自己的方法。如果它是一个单独的分立单元,为什么不标记它并在功能上分解它?这将明确说明块的功能,后来阅读代码的人不必弄清楚。

3

原始示例中的{ ... }的具体位置纯粹用于格式化糖,通过在一组逻辑相关的语句开始以及结束时更明显。如您的示例所示,它对编译的代码没有影响。

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

这是否是一种好的做法是个人喜好的问题。它看起来不错。或者,您可以使用注释和/或缩进来指示代码中语句的逻辑分组,而无需额外使用{ ... }

在这里有各种基于范围的技术,其中一些已经在这里用其他答案进行了说明,但是你在OP中拥有的东西甚至不能远程看这样的东西。再一次,你在OP中拥有的东西(如图所示)纯粹是一种源代码格式化习惯,多余的{ ... }对生成的代码没有任何影响。

+0

似乎你是唯一一个对“编译器错误”问题发表意见的人 - 很好的观察。我的印象是,海报已经看到像这样使用宏: BEGIN_X //做的东西 END_X 其中BEGIN_X和END_X置换包括自己的{和},使得缺少END_X确实能产生编译错误。丑陋的东西。 – 2010-08-26 02:28:52

+0

@Tony:Boost有其中几个。我曾经有大约一百页的错误,因为我忘了一个。 “丑陋的东西”并没有开始描述它:) – 2010-08-26 04:41:46