9

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

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  ...
    }

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

4

9 回答 9

36

这种技术非常常见,被称为设计模式:资源获取即初始化 (RAII)

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

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

如果您担心其他程序员可能不理解它,那么这些程序员应该受过更多的教育。始终努力以最无错误的方式编写代码,这样您和其他人就不可能在自己/自己的脚上开枪。


“一些静态分析工具可以建议将它们删除吗?”

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

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

于 2009-01-12T12:55:30.973 回答
10

正如其他人所说,这是很好的 C++ 风格。为了提高可读性,我总是在此类仅限 RAII 的类前面加上Scoped(例如,ScopedBusyCursor),以便一目了然地清楚该类的目的是什么。

于 2009-01-12T13:41:22.517 回答
7

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

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

#include <boost/noncopyable.hpp>
class BusyCursor : public boost::noncopyable // for scoped use only
{
    // ...
};
于 2009-01-12T13:54:35.330 回答
4

这是一个很好的成语,并且很常用。

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

于 2009-01-12T12:57:51.763 回答
4

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

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

你真的认为在你的代码中乱扔垃圾更利于维护吗?

于 2009-01-12T13:42:46.493 回答
3

没有理智的静态分析工具会建议删除该变量,因为它已被使用。它之所以有效,是因为它的构造函数和析构函数被调用。你应该是绝对安全的。

于 2009-01-12T12:59:48.717 回答
3

其他人已经提到这是经典的 RAII。我要补充的是,这是关于 C++ 最好的事情之一。很少有其他语言支持它,或者至少正确地支持它(甚至 C# 的 using 构造也没有那么好,因为负担仍然在客户端代码上 -请参阅我的博客条目)。

它与 C++ 的联系如此紧密,以至于您应该确信任何阅读它的人都会熟悉它——如果不是的话,他们应该熟悉。

于 2009-01-12T13:36:06.913 回答
2

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

于 2009-01-12T13:14:44.443 回答
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 示例,由于它是一个您可能会重复使用多次的类,因此使用专用类可能会更好。

于 2009-01-16T23:57:20.753 回答