以下の C++ コードでは、 // More コードの実行後に ~obj() デストラクタが呼び出されることが保証されていますか? または、obj オブジェクトが使用されていないことを検出した場合、コンパイラは obj オブジェクトを以前に破棄できますか?
{
SomeObject obj;
... // More code
}
この手法を使用して、ブロックの最後にフラグをリセットすることを忘れないようにしたいのですが、ブロック全体でフラグを設定したままにする必要があります。
これで問題ありません。これは、C++ プログラミングで非常に一般的に使用されるパターンです。C++ 標準セクション 12.4/10 から、デストラクタが呼び出されるタイミングを参照:
オブジェクトが作成されたブロックが終了したときに自動保存期間を持つ構築されたオブジェクトの場合
実際...
C ++には、「あたかも」の原則と呼ばれるものがあります。これらすべての回答で参照されているすべての保証人は、観察可能な行動のみを参照しています。コンパイラーは、観察可能な動作が最初に記述されたとおりに実行されたかのようである限り、任意の関数呼び出しを省略、並べ替え、追加などすることができます。これはデストラクタにも当てはまります。
したがって、技術的には、あなたの観察は正しいです。コンパイラは、オブジェクトが使用されていないことを検出した場合、オブジェクトを早期に破棄することができ、デストラクタまたはそれが呼び出す関数からの観察可能な副作用はありません。ただし、これがデバッガーの外部で発生していることを認識できないことが保証されています。これは、認識できた場合、コンパイラーがそれを実行できなくなるためです。
ただし、コンパイラがこの機能を使用して、デストラクタ呼び出しを実際に並べ替えるのではなく、些細なデストラクタを完全に回避するなどの便利な処理を実行する可能性が高くなります。
編集:誰かが参照を望んでいました... 1.9 / 5、C ++ 0xドラフト標準の脚注4(これは新しい規則ではありません。C++03標準が手元にありません。これも便利です。 C標準に存在する、AFAIK)
1.9 / 5:
整形式プログラムを実行する適合実装は、同じプログラムと同じ入力を持つ抽象マシンの対応するインスタンスの可能な実行シーケンスの1つと同じ観察可能な動作を生成する必要があります。ただし、そのような実行シーケンスに未定義の操作が含まれている場合、この国際規格では、その入力を使用してそのプログラムを実行する実装に要件はありません(最初の未定義の操作に先行する操作に関しても)。
脚注4:
この規定は、「あたかも」ルールと呼ばれることもあります。これは、観察可能な動作から判断できる限り、結果が要件に準拠しているかのようである限り、実装はこの国際規格の要件を自由に無視できるためです。プログラムの。たとえば、実際の実装では、その値が使用されておらず、プログラムの観察可能な動作に影響を与える副作用が発生していないと推測できる場合、式の一部を評価する必要はありません。
私の読書(そして私が一般的な理解だと思った)は、観察可能な動作が元の書かれたソースの動作である限り、これがコンパイラのフリーハンドでやりたいことを実行できるようにする(つまり、最適化を可能にする)ことでした-移動を含むデストラクタの周り、オブジェクトをまったく破壊しない、デストラクタを発明するなど。
オブジェクトがスコープ外になるまで、デストラクタは呼び出されません。
C++ faq liteには、dtors に関する適切なセクションがあります。
C++ での破壊は決定論的です。つまり、コンパイラはそのコードを自由に移動できません。(もちろん、最適化によりデストラクタがインライン化され、デストラクタ コードが相互作用しないと判断され、// More code
命令の並べ替えが行われる場合もありますが、それは別の問題です)
デストラクタが呼び出されるはずのときに呼び出されることに依存できなかった場合、RAII を使用してロックを取得することはできません (または、その点については、他の RAII コンストラクトについても同様です)。
{
LockClass lock(lockData);
// More code
} // Lock automatically released.
また、オブジェクトの構築方法とは逆の順序で実行されるデストラクタに依存することもできます。
はい、保証されています。
自動保存期間を持つオブジェクトの有効期間は、潜在的なスコープの最後ではなく、最後に終了します。このようなオブジェクトの場合、潜在的なスコープは宣言の時点で始まり、宣言されているブロックの最後で終わります。これは、デストラクタが呼び出される瞬間です。
非常に衒学的に言えば、自動化されたオブジェクトであっても、「スコープから外れた」ときに破棄されると言うのは正しくないことに注意してください(「潜在的なスコープから外れる」のではなく)。オブジェクトは何度もスコープの外に出たりスコープに戻ったりする可能性があり (ブロック内で同じ名前のローカル オブジェクトがさらに宣言されている場合)、そのような方法でスコープの外に出てもオブジェクトは破壊されません。自動オブジェクトを殺すのはそのスコープの「最後の最後」であり、上記のように潜在的なスコープの終わりとして定義されています。
実際、言語標準は、自動オブジェクトの存続期間を記述するためにスコープの概念に依存していません(これらすべての複雑な用語に対処する必要はありません)。オブジェクトが定義されているブロックの出口でオブジェクトが破棄されると言っているだけです:)
ここでの回答はすべて、名前付きオブジェクトで何が起こるかを示していますが、完全を期すために、おそらく一時/匿名オブジェクトのルールも知っておく必要があります。(例:f(SomeObjectConstructor()
またはf(someFunctionThatReturnsAnObject())
)
一時オブジェクトは、それらが作成されたポイントを (レキシカルに) 含む完全な式 (1.9) を評価する最後のステップとして破棄されます。これは、その評価が例外のスローで終了した場合でも当てはまります。(ISO C++98 標準の 12.2/3)
これは基本的に、一時的に生成されたオブジェクトが次のステートメントまで存続することを意味します。2 つの例外は、オブジェクトの初期化リストの一部として生成された一時オブジェクト (この場合、オブジェクトが完全に構築された後にのみ一時オブジェクトが破棄される) と、一時オブジェクト (例: const Foo& ref = someFunctionThatReturnsAnobject()
) への参照が行われる場合 (この場合、オブジェクトの有効期間は参照の存続期間)。
はい、C++ 標準には、オブジェクトがいつ破棄されるかについて非常に具体的な要件があります (§12.4/10)。この場合、ブロック内の他のすべてのコードの実行が終了するまでオブジェクトを破棄してはなりません。
あなたの質問が boost::scoped_ptr (または同様の std::auto_ptr) であるように、これの典型的な例:
{
boost::scoped_ptr< MyClass > pMyClass( new MyClass );
// code using pMyClass here
} // destruction of MyClass and memory freed