以下のテキストでは、破棄の時間がそれを囲むスコープ (関数、ブロック、クラス、式) によって静的に決定されるスコープ オブジェクトと、通常は実行時まで正確な破棄の時間が分からない動的オブジェクトを区別します。
クラス オブジェクトの破棄セマンティクスはデストラクタによって決定されますが、スカラー オブジェクトの破棄は常にノーオペレーションです。具体的には、ポインター変数を破棄しても、指示先は破棄されません。
スコープ対象のオブジェクト
自動オブジェクト
自動オブジェクト (一般に「ローカル変数」と呼ばれる) は、制御フローがその定義のスコープを離れると、その定義とは逆の順序で破棄されます。
void some_function()
{
Foo a;
Foo b;
if (some_condition)
{
Foo y;
Foo z;
} <--- z and y are destructed here
} <--- b and a are destructed here
関数の実行中に例外がスローされた場合、以前に構築されたすべての自動オブジェクトは、例外が呼び出し元に伝達される前に破棄されます。このプロセスは、スタックの巻き戻しと呼ばれます。スタックの巻き戻し中に、前述の以前に構築された自動オブジェクトのデストラクタからそれ以上例外が発生することはありません。それ以外の場合、関数std::terminate
が呼び出されます。
これは、C++ で最も重要なガイドラインの 1 つにつながります。
デストラクタはスローしないでください。
非ローカル静的オブジェクト
名前空間スコープで定義された静的オブジェクト (一般に「グローバル変数」と呼ばれます) と静的データ メンバーは、次の実行後に定義とは逆の順序で破棄されますmain
。
struct X
{
static Foo x; // this is only a *declaration*, not a *definition*
};
Foo a;
Foo b;
int main()
{
} <--- y, x, b and a are destructed here
Foo X::x; // this is the respective definition
Foo y;
異なる翻訳単位で定義された静的オブジェクトの構築 (および破棄) の相対的な順序は未定義であることに注意してください。
例外が静的オブジェクトのデストラクタを離れた場合、関数std::terminate
が呼び出されます。
ローカル静的オブジェクト
関数内で定義された静的オブジェクトは、制御フローがその定義を初めて通過するとき (およびその場合) に構築されます。1
の実行後、逆の順序で破棄されますmain
。
Foo& get_some_Foo()
{
static Foo x;
return x;
}
Bar& get_some_Bar()
{
static Bar y;
return y;
}
int main()
{
get_some_Bar().do_something(); // note that get_some_Bar is called *first*
get_some_Foo().do_something();
} <--- x and y are destructed here // hence y is destructed *last*
例外が静的オブジェクトのデストラクタを離れた場合、関数std::terminate
が呼び出されます。
1: これは非常に単純化されたモデルです。静的オブジェクトの初期化の詳細は、実際にはもっと複雑です。
基本クラスのサブオブジェクトとメンバーのサブオブジェクト
制御フローがオブジェクトのデストラクタ本体を離れると、そのメンバ サブオブジェクト (「データ メンバ」とも呼ばれます) は定義とは逆の順序で破棄されます。その後、その基本クラスのサブオブジェクトは、base-specifier-list の逆の順序で破棄されます。
class Foo : Bar, Baz
{
Quux x;
Quux y;
public:
~Foo()
{
} <--- y and x are destructed here,
}; followed by the Baz and Bar base class subobjects
のサブオブジェクトの 1 つの構築中に例外がスローされた場合、Foo
以前に構築されたすべてのサブオブジェクトは、例外が伝播される前に破棄されます。一方Foo
、デストラクタは、オブジェクトが完全に構築されていないため、実行されません。Foo
デストラクタ本体は、データ メンバー自体を破棄する責任を負わないことに注意してください。データ メンバーが、オブジェクトが破棄されたときに解放する必要があるリソース (ファイル、ソケット、データベース接続、ミューテックス、ヒープ メモリなど) へのハンドルである場合にのみ、デストラクタを記述する必要があります。
配列要素
配列要素は降順で破棄されます。n 番目の要素の構築中に例外がスローされた場合、要素 n-1 から 0 は、例外が伝搬される前に破棄されます。
一時オブジェクト
クラス型の prvalue 式が評価されると、一時オブジェクトが構築されます。prvalue 式の最も顕著な例は、 などの値によってオブジェクトを返す関数の呼び出しですT operator+(const T&, const T&)
。通常の状況では、prvalue を字句的に含む完全な式が完全に評価されると、一時オブジェクトは破棄されます。
__________________________ full-expression
___________ subexpression
_______ subexpression
some_function(a + " " + b);
^ both temporary objects are destructed here
上記の関数呼び出しsome_function(a + " " + b)
は、より大きな式の一部ではない (式ステートメントの一部である) ため、完全な式です。したがって、部分式の評価中に構築されるすべての一時オブジェクトは、セミコロンで破棄されます。このような一時オブジェクトは 2 つあります。1 つ目は最初の追加時に作成され、2 つ目は 2 回目の追加時に作成されます。2 番目の一時オブジェクトは、最初のオブジェクトの前に破棄されます。
2 回目の追加中に例外がスローされた場合、最初の一時オブジェクトは、例外が伝搬される前に適切に破棄されます。
ローカル参照が prvalue 式で初期化されている場合、一時オブジェクトの有効期間はローカル参照のスコープまで延長されるため、ダングリング参照は取得されません。
{
const Foo& r = a + " " + b;
^ first temporary (a + " ") is destructed here
// ...
} <--- second temporary (a + " " + b) is destructed not until here
非クラス型の prvalue 式が評価される場合、結果はvalueであり、一時オブジェクトではありません。ただし、 prvalue を使用して参照を初期化すると、一時オブジェクトが構築されます。
const int& r = i + j;
動的オブジェクトと配列
次のセクションでは、Xの破壊は「まず X を破壊し、次に基になるメモリを解放する」ことを意味します。同様にcreate Xは、「最初に十分なメモリを割り当ててから、そこに X を構築する」ことを意味します。
動的オブジェクト
によって作成された動的オブジェクトは、p = new Foo
によって破棄されdelete p
ます。を忘れるとdelete p
、リソース リークが発生します。次のいずれかを実行しようとしないでください。これらはすべて未定義の動作につながるためです。
delete[]
(角括弧に注意してください)、free
またはその他の手段を介して動的オブジェクトを破棄します
- 動的オブジェクトを複数回破壊する
- 動的オブジェクトが破棄された後にアクセスする
動的オブジェクトの構築中に例外がスローされた場合、例外が伝播される前に基になるメモリが解放されます。(オブジェクトが完全に構築されていないため、デストラクタはメモリ解放前に実行されません。)
動的配列
via で作成された動的配列は、viap = new Foo[n]
で破棄されdelete[] p
ます (角括弧に注意してください)。を忘れるとdelete[] p
、リソース リークが発生します。次のいずれかを実行しようとしないでください。これらはすべて未定義の動作につながるためです。
delete
、free
またはその他の手段を介して動的配列を破棄します
- 動的配列を複数回破棄する
- 動的配列が破棄された後にアクセスする
n 番目の要素の構築中に例外がスローされた場合、n-1 から 0 までの要素が降順に破棄され、基になるメモリが解放され、例外が伝播されます。
(通常、動的配列よりも優先する必要がありstd::vector<Foo>
ます。これにより、正確で堅牢なコードをより簡単に記述できます。)Foo*
参照カウント スマート ポインター
複数のオブジェクトによって管理される動的オブジェクトは、その動的オブジェクトの共有に関係std::shared_ptr<Foo>
する最後のオブジェクトの破棄中に破棄されます。std::shared_ptr<Foo>
(通常、共有オブジェクトよりも優先する必要がありstd::shared_ptr<Foo>
ます。これにより、正しく堅牢なコードをより簡単に記述できます。)Foo*