好的还是坏的C ++成语 - 纯粹用于构造函数/析构函数的对象?

时间:2009-01-12 12:50:21

标签: c++ idioms raii

我有一些类除了构造函数/析构函数外什么都不做。这是一个例子

class BusyCursor 
{
  private:
    Cursor oldCursor_;

  public:

    BusyCursor()
    {
      oldCursor_ = CurrentCursor();
      SetCursor(BUSY_CURSOR);
    }
    ~BusyCursor()
    {
      SetCursor(oldCursor_);
    }
}

// example of use
    void DoSlowThing
    {
      BusyCursor busy;
      ... do something time-consuming  ...
    }

我有点担心未来的可读性。我在这里过于“狡猾”,有一个变量(“忙”)从未在代码中实际使用过吗?一些静态分析工具是否可以建议删除它们,或者这个习惯用法是否足够常见而不用担心?

9 个答案:

答案 0 :(得分:36)

这种技术很常见,被称为设计模式:Resource Acquisition Is Initialization (RAII)

我会毫不犹豫地使用这种设计模式。

使用此设计模式进行编码要好得多,因为您可以通过忘记重置光标或任何有问题的资源来避免错误。

如果您担心其他程序员可能不理解它,那么那些程序员应该受到更多教育。始终努力以最无差错的方式进行编码,使您和其他人无法在脚下拍摄自己/自己。


“某些静态分析工具是否可以建议删除它们?”

  • 没有静态分析工具会将此视为问题。
  • 不会给出编译器警告
  • 没有编译器优化会导致任何问题。

原因是因为创建了对象并且调用了构造函数/析构函数。所以它不是一个未引用的变量。

答案 1 :(得分:10)

正如其他人所说,这是一个很好的C ++风格。为了提高可读性,我总是将这些仅限RAII的类添加Scoped(例如,ScopedBusyCursor),以便一目了然地了解该类的用途。

答案 2 :(得分:7)

这是一个众所周知的好C ++习语,就像其他人一样回答。

为了清楚地表明这些类只是在一个范围内使用而不是在不同的范围之间移动,最好使它们不可复制。这可以通过添加未实现的私有拷贝构造函数和拷贝赋值运算符来手动完成。更短且更易读的方法是从boost::noncopyable

派生类
#include <boost/noncopyable.hpp>
class BusyCursor : public boost::noncopyable // for scoped use only
{
    // ...
};

答案 3 :(得分:4)

这是一个很好的习惯用法。

它比任何替代方法都好,例如,即使你的某些耗时的代码抛出异常,仍然会调用~BusyCursor析构函数。

答案 4 :(得分:4)

可以说不使用这种模式是不好的习惯用法。当您不使用RAII时,您的代码最终看起来像这样:

void func() {
    Cursor oldCursor = CurrentCursor();
    SetCursor(BUSY_CURSOR);
    try {
        do_slow_stuff();
        SetCursor(oldCursor);
    } catch (...) {
        SetCursor(oldCursor);
        throw;
    }
}

你是否真的认为在整个代码中散落这些内容对维护更好?

答案 5 :(得分:3)

没有理智的静态分析工具会建议删除变量,因为使用了item> 。它有效,因为它的构造函数和析构函数被调用。你应该非常安全。

答案 6 :(得分:3)

其他人已经提到这是经典的RAII。 我要补充的是,这是关于C ++的最好的事情之一。很少有其他语言支持它,或者至少支持它(即使C#使用构造也不是很好,因为负担仍然在客户端代码上 - see my blog entry on this)。

它与C ++密切相关,你应该确信任何阅读它的人都会熟悉它 - 如果不是,那么它们应该是。

答案 7 :(得分:2)

我通常称之为“守卫”。在我看来,它展示了C ++最大的优势之一(确定性资源处理)。这是我在垃圾收集语言中工作时最想念的事情之一。

答案 8 :(得分:0)

你也可以选择像Andrei Alexandrescu和Petru Marginean这样的ScopeGuard。您的样本看起来就像这样:

void DoSlowThing
{     
    Cursor oldCursor = CurrentCursor();
    SetCursor(BUSY_CURSOR);
    ON_BLOCK_EXIT(SetCursor, oldCursor);
    ... do something time-consuming  ...
}

这使得更容易进行一次性RAII类型操作,而无需为每个操作创建新类。但是对于你的Cursor示例,因为它是一个你可能会重复使用很多次的类,所以你可能最好使用专用类。