2009-09-22 11 views
7

私はRAIIスタイルのC++クラスがあるとします。RAII形式のクラスが「匿名で」インスタンス化されないようにすることはできますか?

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、他のコンパイラのアイデアを歓迎しますが有用ではありません)ので、それらのいずれかのハックが役に立つでしょう(適切なifdef'dマクロ定義に抽象化されています) 。

+0

SaveMatrix(): - >私への関数呼び出しのように見えます。 –

+0

SaveMatrix save(): - >私の関数宣言のように見えます。 –

答えて

3

私は実際にワルドが投稿バリアントからの方法の束でmy solutionを微調整しなければならなかったが、私は最終的になったことのマクロ化したバージョンです:

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(); 
    } 
}; 
6

SaveMatrix save();のいずれもオブジェクトを定義しません。それは関数を宣言します。

他人(またはあなた自身、FTM)が望む以外のことをしないようにできることはごくわずかです。私が考えることができるのは、コード自体を書くのではなく、代わりにマクロを書くことです。

#define SAVE_MATRIX SaveMatrix save ## __LINE__ 

ただし、これはかなり醜いです。 OTOH、それはコンパイル時にエラーをキャッチします。

9

コンパイル時に何かができるかどうかわかりません。

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

書くためにクライアントを必要とする:実行時のチェックのために、あなたはこれを行うことができ

SaveMatrix sm(sm); 

を識別子にそれを結合することなく、一時的に同じ操作を行うための方法はありません(その時点で、それは自動変数と変わりありません)。

+2

最初はちょっと変わっていますが、RAIIのイディオムの一部になる可能性があります。なぜなら、この質問はC++/RAIIの実際の問題を示しているからです。むしろ、私のロックがこれではうまくいかないことを知る上で、ちょっとしたことがありました。 – stefaanv

+0

ええと、オプションの私はこれが最高ののがいいと思います。それはまったく自然ではありませんが、RAII自体は、少なくともクラスを主にstructs-with-initialization-and-cleanupと考えるのではなく、全く自然ではありません。ありがとう! –

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; 
} 
+1

'RAII()。Do()'が失敗するのはここでは問題ないようです。 AFAIU、Opは、マキアヴェッリではなく、マーフィーの男をシャットアウトしようとします。 – sbi

関連する問題