代码之家  ›  专栏  ›  技术社区  ›  bobobobo

C++代码中的额外括号括号

  •  13
  • bobobobo  · 技术社区  · 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. 如果它不是一个,它会改变代码的编译方式还是使其变慢?
    8 回复  |  直到 7 年前
        1
  •  31
  •   GManNickG    14 年前

    支架本身很好,它们所做的只是限制范围,并且您不会减慢任何操作。它可以被视为清洁剂。(总是喜欢干净的代码而不是快速的代码,如果干净的话,在分析之前不要担心速度。)


    但在资源方面,这是一个糟糕的做法,因为你已经让自己处于一个泄露资源的位置。如果块中有任何东西抛出或返回,砰,你就死定了。

    使用范围绑定资源管理(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
    }
    

    这样做会 全部的 资源,更干净更安全。如果你能说“我需要释放这个资源”,你就错了,它们应该被自动处理。

        2
  •  17
  •   sbi    14 年前

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

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

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

        3
  •  3
  •   tenfour    14 年前

    这不是坏做法。它不会使任何事情变慢;它只是一种构造代码的方法。

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

        4
  •  3
  •   AnT stands with Russia    14 年前

    特定位置 { ... } 在您的原始示例中,通过使一组逻辑相关的语句从何处开始和从何处结束变得更加明显,可以纯粹地充当格式化sugar的角色。如示例所示,它对编译后的代码没有影响。

    我不知道你所说的“如果互斥体没有释放,这会导致编译器错误”。这根本不是真的。这样的使用 {…} 不能也不会引入任何编译器错误。

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

    这里有各种基于范围的技术,其中一些已经在这里的其他答案中得到了说明,但是你在你的操作中所拥有的甚至根本不像那样。再一次,您的操作(如图所示)中的内容纯粹是一个带有多余内容的源格式习惯。 {…} 对生成的代码没有影响。

        5
  •  2
  •   Mike Seymour    14 年前

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

    就我个人而言,我将其称为坏做法;避免您在这里可能犯的错误的方法是使用范围资源管理(有时称为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()
    
        6
  •  1
  •   Starkey    14 年前

    任何提高可读性的东西都是很好的练习。如果添加大括号有助于提高可读性,那么就开始吧!

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

        7
  •  1
  •   joshperry    14 年前

    在C++中使用对象析构函数是非常有用的(imHO);

    想象一下,如果您创建了一个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
    
        8
  •  1
  •   C8H10N4O2    7 年前

    如果要将代码放入大括号中,您可能应该将其分解为自己的方法。如果它是一个单独的单元,为什么不给它贴上标签,在功能上把它拆开呢?这将使它明确块的作用,以后读取代码的人就不必知道了。