Destructors
デストラクタは、オブジェクトの 寿命 が終了する際に呼び出される特別な メンバー関数 です。デストラクタの目的は、オブジェクトがその寿命中に獲得した可能性のあるリソースを解放することです。
|
デストラクタは コルーチン にすることはできません。 |
(C++20以降) |
目次 |
構文
デストラクタ (C++20まで) プロスペクティブデストラクタ (C++20以降) は以下の形式のメンバー 関数宣言子 を使用して宣言されます:
class-name-with-tilde
(
parameter-list
(オプション)
)
except
(オプション)
attr
(オプション)
|
|||||||||
| class-name-with-tilde | - | 識別子式、 属性のリストが続く可能性があり、 (C++11以降) 括弧の組で囲まれる可能性がある | ||||||
| parameter-list | - | パラメータリスト (空または void でなければならない) | ||||||
| except | - |
|
||||||
| attr | - | (C++11以降) 属性のリスト 属性 |
デストラクタの宣言における
宣言指定子
で許可されている唯一の指定子は、
プロスペクティブ
(C++20以降)
デストラクタ宣言では、
constexpr
,
(C++11以降)
friend
,
inline
および
virtual
です(特に、戻り値の型は許可されていません)。
class-name-with-tilde の識別子式は、以下のいずれかの形式でなければなりません:
-
- クラスに対しては、識別子式は ~ に続けて直近の外側クラスの injected-class-name となる。
- クラステンプレートに対しては、識別子式は ~ に続けて 現在のインスタンス化を指すクラス名 current instantiation (C++20まで) injected-class-name (C++20以降) となる。
- それ以外の場合、識別子式は、修飾識別子の非終端部分によって指名されるクラスの注入クラス名の前に ~ が付いた終端非修飾識別子を持つ修飾識別子である。
説明
デストラクタは、オブジェクトの 寿命 が終了する際に暗黙的に呼び出されます。これには以下の場合が含まれます:
- プログラム終了 , 静的 storage duration を持つオブジェクトに対して
|
(C++11以降) |
- スコープの終了時、自動記憶域期間を持つオブジェクトおよび参照にバインドすることで寿命が延長された一時オブジェクトに対して
- delete 式 、動的記憶域期間を持つオブジェクトに対して
- 完全な 式 の終了時、名前のない一時オブジェクトに対して
- スタックアンワインディング 、例外がブロックから抜けて捕捉されない場合の自動記憶域期間を持つオブジェクトに対して
デストラクタは明示的に呼び出すことも可能です。
プロスペクティブデストラクタクラスは1つ以上のプロスペクティブデストラクタを持つことができ、そのうちの1つがクラスのデストラクタとして選択されます。 どのプロスペクティブデストラクタがデストラクタであるかを決定するために、クラス定義の終了時に、 オーバーロード解決 が空の引数リストでクラス内で宣言されたプロスペクティブデストラクタ間で実行されます。オーバーロード解決が失敗した場合、プログラムは不適格となります。デストラクタの選択は選択されたデストラクタを odr-use せず、選択されたデストラクタは削除されている可能性があります。
すべてのプロスペクティブデストラクタは特殊メンバ関数です。クラス
このコードを実行
#include <cstdio> #include <type_traits> template<typename T> struct A { ~A() requires std::is_integral_v<T> { std::puts("~A, T is integral"); } ~A() requires std::is_pointer_v<T> { std::puts("~A, T is a pointer"); } ~A() { std::puts("~A, T is anything else"); } }; int main() { A<int> a; A<int*> b; A<float> c; } 出力: ~A, T is anything else ~A, T is a pointer ~A, T is integral |
(C++20以降) |
潜在的に呼び出されるデストラクタ
クラス
T
のデストラクタは、以下の状況で
潜在的に呼び出される可能性があります
:
- 明示的または暗黙的に呼び出される。
-
new
式
が型
Tのオブジェクトの配列を作成する。 -
return
文
の結果オブジェクトが型
Tである。 -
配列が
集成体初期化
の対象であり、その要素型が
Tである。 -
クラスオブジェクトが集成体初期化の対象であり、型
Tのメンバを持ち、Tが 匿名共用体 型ではない。 -
潜在的に構築される部分オブジェクト
が
非
委譲
(C++11以降)
コンストラクタ内で型
Tである。 -
型
Tの 例外オブジェクト が構築される。
呼び出される可能性のあるデストラクタが 削除済みまたは (C++11以降) 呼び出しのコンテキストからアクセスできない場合、プログラムは不適格となる。
暗黙的に宣言されるデストラクタ
ユーザー宣言された プロスペクティブ (C++20以降) デストラクタが クラス型 に対して提供されない場合、コンパイラは常にデストラクタをそのクラスの inline public メンバーとして宣言します。
他の暗黙的に宣言される特別なメンバ関数と同様に、暗黙的に宣言されるデストラクタの例外仕様は、非スロー(non-throwing)です。ただし、 構築される可能性のある基底クラスまたはメンバのデストラクタが 潜在的にスローする可能性がある場合 (C++17以降) 暗黙的な定義が異なる例外仕様を持つ関数を直接呼び出す場合 (C++17以前) を除きます。実際には、暗黙的なデストラクタは noexcept であり、デストラクタが noexcept ( false ) である基底クラスまたはメンバによって「汚染」されたクラスでない限り、その限りではありません。
暗黙的に定義されるデストラクタ
暗黙的に宣言されたデストラクタが削除されない場合、それが odr-used されるときに、コンパイラによって暗黙的に定義されます(つまり、関数本体が生成されコンパイルされます)。この暗黙的に定義されたデストラクタは空の本体を持ちます。
|
これが constexprデストラクタ (C++23まで) constexpr関数 (C++23以降) の要件を満たす場合、生成されるデストラクタは constexpr となります。 |
(C++20以降) |
削除されたデストラクタ
クラス
|
(C++11以降) |
自明なデストラクタ
T
クラスのデストラクタは、以下のすべての条件を満たす場合にトリビアルとなります:
- デストラクタは 暗黙的に宣言される (C++11まで) ユーザー提供ではない user-provided (C++11以降) 。
- デストラクタは仮想ではない。
- すべての直接基底クラスは自明なデストラクタを持つ。
|
(C++26まで) |
|
(C++26以降) |
自明なデストラクタは何も処理を行わないデストラクタです。自明なデストラクタを持つオブジェクトは delete 式を必要とせず、単にその記憶域を解放するだけで破棄できます。C言語と互換性のあるすべてのデータ型(POD型)は自明に破棄可能です。
デストラクションシーケンス
ユーザー定義または暗黙的に定義されたデストラクタの両方について、デストラクタの本体を実行し、本体内で割り当てられた自動オブジェクトを破棄した後、コンパイラはクラスの非静的で非バリアントのデータメンバすべてのデストラクタを宣言の逆順で呼び出し、次にすべての直接の非仮想基底クラスのデストラクタを 構築の逆順 で呼び出し(それらがさらに自身のメンバと基底クラスのデストラクタを呼び出す)、そして、このオブジェクトが most derived class である場合、すべての仮想基底クラスのデストラクタを呼び出します。
デストラクタが直接呼び出された場合でも(例: obj.~Foo ( ) ; )、 return 文は ~Foo ( ) 内で呼び出し元に直ちに制御を戻すわけではありません:最初にすべてのメンバーおよび基底デストラクタを呼び出します。
仮想デストラクタ
基底クラスへのポインタを通じたオブジェクトの削除は、基底クラスのデストラクタが virtual でない限り未定義動作を引き起こします:
class Base { public: virtual ~Base() {} }; class Derived : public Base {}; Base* b = new Derived; delete b; // 安全
一般的なガイドラインとして、基底クラスのデストラクタは publicかつvirtual、あるいはprotectedかつnonvirtualのいずれかでなければならない 。
純粋仮想デストラクタ
見込みの プロスペクティブ (C++20以降) デストラクタは 純粋仮想 として宣言することができます。例えば、抽象クラスにする必要があるが、他に純粋仮想として宣言できる適切な関数がない基底クラスなどです。純粋仮想デストラクタは定義を持たなければなりません。なぜなら、派生クラスが破棄されるときには常にすべての基底クラスデストラクタが呼び出されるからです。
class AbstractBase { public: virtual ~AbstractBase() = 0; }; AbstractBase::~AbstractBase() {} class Derived : public AbstractBase {}; // AbstractBase obj; // コンパイルエラー Derived obj; // OK
例外
他の関数と同様に、デストラクタも 例外 を送出して終了することができます (通常、これは明示的に noexcept ( false ) と宣言する必要があります) (C++11以降) 。しかし、このデストラクタが スタックアンワインディング 中に呼び出された場合、 std::terminate が代わりに呼び出されます。
ただし、 std::uncaught_exceptions がスタックアンワインディングの検出に使用される場合があるものの、デストラクタが例外を送出して終了することを許可することは一般的に悪いプラクティスと見なされます。この機能は、一時オブジェクトを構築する完全式の終了時に無名一時オブジェクトのデストラクタが例外を送出する能力に依存する SOCI や Galera 3 などの一部のライブラリで使用されています。
std::experimental::scope_success は、Library fundamental TS v3において 例外を送出する可能性のあるデストラクタ を持つ場合があります。これは、スコープが正常に終了し、かつ終了関数が例外を送出した場合に例外を送出します。
注記
通常のオブジェクト(ローカル変数など)に対してデストラクタを直接呼び出すと、スコープ終了時にもう一度デストラクタが呼び出されるため、未定義動作を引き起こします。
ジェネリックな文脈では、非クラス型のオブジェクトに対してもデストラクタ呼び出し構文を使用できます。これは擬似デストラクタ呼び出しとして知られています。詳細は member access operator を参照してください。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_trivial_union
|
202502L
|
(C++26) | 共用体の特殊メンバー関数に対する自明性要件の緩和 |
例
出力:
ctor a0 ctor a1 ctor a2 ctor a3 dtor a2 dtor a3 dtor a1 dtor a0
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 193 | C++98 |
デストラクタ内の自動オブジェクトが基底クラスおよびメンバー部分オブジェクトの破棄
よりも前か後で破棄されるかが未規定 |
これらの部分オブジェクトを破棄する前
に破棄される |
| CWG 344 | C++98 | デストラクタの宣言子構文に欠陥があった( CWG 194 および CWG 263 と同様の問題) | 専用の関数宣言子構文に変更 |
| CWG 1241 | C++98 |
静的メンバーがデストラクタ実行直後に
破棄される可能性があった |
非静的メンバーのみ
を破棄する |
| CWG 1353 | C++98 |
暗黙宣言されたデストラクタが未定義となる条件に
多次元配列型が考慮されていなかった |
これらの型を考慮する |
| CWG 1435 | C++98 |
デストラクタの宣言子構文における「クラス名」の
意味が不明確だった |
専用の関数宣言子構文に変更 |
| CWG 2180 | C++98 |
最終派生クラスでないクラスのデストラクタが
仮想直接基底クラスのデストラクタを呼び出していた |
これらのデストラクタを呼び出さない |
| CWG 2807 | C++20 | 宣言指定子に consteval を含むことができた | 禁止 |