8

次のような同期プリミティブの所有権を表すクラスを作成することを考えています。

class CCriticalSectionLock
{
public:
    CCriticalSectionLock( CCriticalSection &cs ) : cs( cs )
    { cs.Enter(); }
    ~CCriticalSectionLock()
    { cs.Leave(); }
private:
    CCriticalSection &cs;
};

これは、関数中に所有権を取得し、複数の出口点または例外がある場合でも所有権を解放できるようにするための良い方法のように見えます。ただし、コンパイラがさまざまなものを評価する正確なタイミングについて、いくつかの微妙な問題が発生します。次の使用を検討してください。

int MyMethod( void )
{
    not_locked(); // do something not under lock

    CCriticalSectionLock myLock( someCriticalSection );

    locked(); // do something under lock

    return ...; // some expression
}

私の知る限り、C++ のライフタイム ルールは、ロックが取得されるnot_locked()に呼び出されること、およびロックが保持されている間に呼び出されることを保証します。locked()

ただし、ロック デストラクタが呼び出されるポイントに関して、返される式がいつ評価されるかについては、はっきりしていません。デストラクタの前に式が評価されることが保証されていますか? 私はそう思いますが、100% 確信があるわけではありません。そうでなければ、非常に微妙で、断続的で、見つけにくいバグにつながる可能性があります。

4

1 に答える 1

8

そうでない場合、それは非常に問題になります。

実際、次のコードを検討してください。

int function(){

    MyClass myObject;
    //stuff
    return 5 + myObject.getNumericalValue();
}

getNumericalValue()メンバー変数の計算に基づいて int を返す単純なメンバー関数を使用します。の破棄後に式が評価された場合、myObject未定義の動作が発生し、return ステートメントでローカルを使用することはできません (そうではありません)。

あなたの場合、ロックはreturn ステートメントの評価後に破棄されます。

それにいくつかの厳密さを追加するために、標準を引用させてください(§3.7.3 / 3、強調鉱山):

自動保存期間を持つ変数に初期化または副作用のあるデストラクタがある場合、そのブロックが終了する前に破棄してはならず、使用されていないように見えても最適化として削除してはなりません

関数の場合、ブロックの終わりはreturn ステートメントの後です。

于 2013-09-27T08:59:30.857 に答える