7

假设我有一个 RAII 风格的 C++ 类:

class StateSaver
{
  public:
    StateSaver(int i) { saveState(); }
    ~StateSaver() { restoreState(); }
};

...在我的代码中像这样使用:

void Manipulate()
{
  StateSaver save(1);

  // ...do stuff that modifies state
}

...目标是进入某个状态,做一些事情,然后在我离开那个范围时离开那个状态。有没有办法使这个错字不编译(或警告,或以某种方式抱怨以便注意到错误)?

void Manipulate()
{
  StateSaver(1); // ruh-roh, state saved and immediately restored!

  // ...do stuff that modifies state
}

我不知道 C++ 本身有什么东西可以用来防止这种情况发生,但这并不意味着它不存在。如果 C++ 中没有任何内容,则可以接受特定于编译器的扩展。我主要对任何针对 gcc 和 msvc 的东西感兴趣(有一天 icc,其他编译器的想法很受欢迎,但不太可能有用),因此对它们中的任何一个的 hack 都会很有用(当然,适当地抽象为 #ifdef'd 宏定义) .

4

4 回答 4

10

我不确定是否可以在编译时完成任何事情。对于运行时检查,您可以这样做:

struct SaveMatrix
{
    SaveMatrix(const SaveMatrix& that) {
        assert(this == &that);
        glPushMatrix();
    }
    ~SaveMatrix() { glPopMatrix(); }
};

这需要客户端编写:

SaveMatrix sm(sm);

如果不将临时变量绑定到标识符(此时它与自动变量没有区别),就无法对临时变量执行相同的操作。

于 2009-09-22T23:35:37.760 回答
6

SaveMatrix save();也没有定义对象。它声明了一个函数。

您几乎无法阻止其他人(或您自己,FTM)做他们想做的事情。我唯一能想到的不是编写代码本身,而是编写一个宏。

#define SAVE_MATRIX SaveMatrix save ## __LINE__

然而,这很丑陋。OTOH,它确实在编译时捕获了错误。

于 2009-09-22T23:31:41.070 回答
3

实际上,我不得不从 Waldo 发布的变体中以多种方式调整我的解决方案,但我最终得到的是一个宏化版本:

class GuardNotifier
{
    bool* notified;
  public:
    GuardNotifier() : notified(NULL) { }
    void init(bool* ptr) { notified = ptr; }
    ~GuardNotifier() { *notified = true; }
};
class GuardNotifyReceiver
{
    bool notified;
  public:
    GuardNotifyReceiver() : notified(false) { }
    void init(const GuardNotifier& notifier)
      { const_cast<GuardNotifier&>(notifier).init(&notified); }
    ~GuardNotifyReceiver() { assert(notified); }
};
class StateSaver
{
    GuardNotifyReceiver receiver;
  public:
    StateSaver(int i,
               const GuardNotifier& notifier = GuardNotifier())
    {
      receiver.init(notifier)
      saveState();
    }
    ~StateSaver()
    {
      restoreState();
    }
};
于 2009-09-24T23:00:12.850 回答
1

该类永远无法判断它是作为临时实例 (SaveMatrix()) 还是作为变量 (SaveMatrix save;) 实例化的。我认为阻止程序员在没有堆栈或宏黑客的情况下这样做的最好方法是在构造后强制调用成员函数,例如:

class RAII
{
public:
    bool valid;
    RAII()
        : valid(false)
    {
        cout << "RAII ctor" << endl;
    }

    void Do()
    {
        valid = true;
    }

    ~RAII()
    {
        assert(valid);
        cout << "RAII dtor" << endl;
    }
};

然后按如下方式工作:

{
    // Intended use
    RAII raii;
    raii.Do();

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII raii;

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII();

    cout << "Some task" << endl;
}

{
    // Programmer shot self in foot, hopefully the act of typing this would make them realise that
    RAII().Do();

    cout << "Some task" << endl;
}
于 2009-09-23T00:25:31.403 回答