名前空間
変種
操作

デストラクタ

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
コピー代入
ムーブ代入 (C++11)
デストラクタ
テンプレート
その他
 
 

デストラクタは、オブジェクトの寿命が終わったときに呼び出される特別なメンバ関数です。デストラクタの目的は、オブジェクトがその寿命中に取得した可能性のあるリソースを解放することです。

デストラクタはコルーチンにはできません。

(C++20以降)

目次

[編集] 構文

デストラクタ(C++20まで)候補デストラクタ(C++20以降) は、次の形式のメンバ関数宣言子を使用して宣言されます。

クラス名とチルダ ( パラメータリスト (任意) ) 例外仕様 (任意) 属性 (任意)
クラス名とチルダ - または、一次式オプションで属性のリストが続き、(C++11以降)オプションで括弧のペアで囲まれた識別子
parameter-list - パラメータリスト (空またはvoidである必要があります)
except -

動的例外仕様

(C++11まで)

動的例外指定
またはnoexcept指定

(C++11以降)
(C++17まで)

noexcept指定

(C++17以降)
attr - (C++11以降) 属性のリスト

デストラクタ宣言の宣言指定子で許可されるのは、候補(C++20以降) デストラクタ宣言のconstexpr(C++11以降)friendinline、およびvirtual (特に、戻り値の型は許可されません) です。

クラス名とチルダの識別子式は、次のいずれかの形式である必要があります。

  • クラスの場合、識別子式は~の後に、直接囲むクラスの注入クラス名が続きます。
  • クラステンプレートの場合、識別子式は~の後に、現在のインスタンスを名前とするクラス名(C++20まで)直接囲むクラステンプレートの注入クラス名(C++20以降)が続きます。
  • それ以外の場合、識別子式は、修飾識別子の末尾の非修飾識別子が~で、その後に修飾識別子の非終端部分によって指名されたクラスの注入クラス名が続く、修飾識別子です。

[編集] 説明

デストラクタは、オブジェクトの寿命が終わるときに暗黙的に呼び出されます。これには以下が含まれます。

  • スレッドローカル記憶域期間を持つオブジェクトのスレッド終了
(C++11以降)
  • 自動記憶域期間を持つオブジェクトおよび参照へのバインディングによって寿命が延長された一時オブジェクトのスコープ終了
  • 動的記憶域期間を持つオブジェクトのdelete
  • 無名一時オブジェクトの完全な式の終了
  • 例外がブロックをエスケープし、捕捉されなかった場合の自動記憶域期間を持つオブジェクトのスタック巻き戻し

デストラクタは明示的に呼び出すこともできます。

候補デストラクタ

クラスは1つ以上の候補デストラクタを持つことができ、そのうちの1つがクラスのデストラクタとして選択されます。

どの候補デストラクタがデストラクタであるかを決定するために、クラス定義の終わりに、クラスで宣言された空の引数リストを持つ候補デストラクタ間でオーバーロード解決が実行されます。オーバーロード解決が失敗した場合、プログラムは不正な形式になります。デストラクタの選択は、選択されたデストラクタをodr-useせず、選択されたデストラクタは削除される場合があります。

すべての候補デストラクタは特殊メンバ関数です。クラスTにユーザー宣言された候補デストラクタが提供されていない場合、コンパイラは常に1つを暗黙宣言し、暗黙宣言された候補デストラクタもTのデストラクタとなります。

#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のデストラクタは、次のような状況で*潜在的に呼び出されます*。

潜在的に呼び出されるデストラクタが削除されているか、(C++11以降)呼び出しのコンテキストからアクセスできない場合、プログラムは不正な形式になります。

[編集] 暗黙宣言デストラクタ

ユーザー宣言された候補(C++20以降)デストラクタがクラス型に提供されていない場合、コンパイラは常にそのクラスのinline publicメンバとしてデストラクタを宣言します。

暗黙宣言された特殊メンバ関数と同様に、暗黙宣言されたデストラクタの例外仕様は、潜在的に構築される基底クラスまたはメンバのデストラクタが例外を投げうる場合(C++17以降)暗黙定義が異なる例外仕様を持つ関数を直接呼び出す場合(C++17まで)を除き、例外を投げません。実際には、暗黙デストラクタは、クラスが例外を投げないデストラクタを持つ基底クラスまたはメンバによって「汚染」されていない限り、noexceptです。

[編集] 暗黙定義デストラクタ

暗黙宣言されたデストラクタが削除されていない場合、それはodr-usedされると、コンパイラによって暗黙的に定義されます(つまり、関数本体が生成されコンパイルされます)。この暗黙定義されたデストラクタは空の本体を持ちます。

これがconstexprデストラクタ(C++23まで)constexpr関数(C++23以降) の要件を満たす場合、生成されたデストラクタはconstexpr になります。

(C++20以降)


削除済みデストラクタ

クラスTの暗黙宣言または明示的にデフォルト設定されたデストラクタは、以下のいずれかの条件が満たされた場合に削除済みとして定義されます。

  • 削除されているか、Tのデストラクタからアクセスできない、または
  • サブオブジェクトがバリアントメンバである場合、非自明である。
(C++26まで)
  • Tは共用体ではなく、クラス型M(またはその多次元配列)の非バリアント潜在的に構築されるサブオブジェクトを持ち、そのMTのデストラクタから削除されているかアクセスできないデストラクタを持つ。
  • Tは共用体であり、以下のいずれかの条件が満たされる。
  • Tのオブジェクトをデフォルト初期化するためにコンストラクタを選択するオーバーロード解決が失敗するか、または削除済みもしくは非自明なコンストラクタを選択する。
  • Tはクラス型M(またはその多次元配列)のバリアントメンバVを持ち、そのVはデフォルト初期化子を持ち、Mは非自明なデストラクタを持つ。
(C++26以降)
  • デストラクタが仮想であり、解放関数の検索が以下のような結果になる。
  • あいまいさ、または
  • 削除済みまたはデストラクタからアクセスできない関数。

Tの明示的にデフォルト設定された候補デストラクタは、Tのデストラクタではない場合、削除済みとして定義されます。

(C++20以降)
(C++11以降)

[編集] 自明デストラクタ

クラスTのデストラクタは、以下のすべての条件が満たされた場合に自明とみなされます。

  • デストラクタは暗黙宣言されている(C++11まで)ユーザー提供されていない(C++11以降)
  • デストラクタは仮想ではない。
  • すべての直接基底クラスは自明なデストラクタを持つ。
  • クラス型(またはクラス型の配列)のすべての非静的データメンバは、自明なデストラクタを持つ。
(C++26まで)
  • Tが共用体であるか、またはクラス型(またはクラス型の配列)のすべての非バリアントな非静的データメンバが自明なデストラクタを持つ。
(C++26以降)

自明なデストラクタは、何のアクションも実行しないデストラクタです。自明なデストラクタを持つオブジェクトは、delete 式を必要とせず、単にストレージを解放することで破棄できます。C言語と互換性のあるすべてのデータ型(POD型)は、自明に破棄可能です。

[編集] 破棄シーケンス

ユーザー定義または暗黙定義されたデストラクタの場合、デストラクタ本体の実行と、本体内で割り当てられた自動オブジェクトの破棄後、コンパイラはクラスのすべての非静的非バリアントデータメンバのデストラクタを宣言順の逆順で呼び出し、次にすべての直接非仮想基底クラスのデストラクタを構築順の逆順で呼び出します(これらはさらにメンバや基底クラスのデストラクタなどを呼び出します)。その後、このオブジェクトが最も派生したクラスである場合、すべての仮想基底クラスのデストラクタを呼び出します。

デストラクタが直接呼び出された場合(例:obj.~Foo();)でも、~Foo()return 文はすぐに呼び出し元に制御を返しません。まず、これらのすべてのメンバおよび基底デストラクタを呼び出します。

[編集] 仮想デストラクタ

基底クラスへのポインタを通じてオブジェクトを削除すると、基底クラスのデストラクタが仮想でない限り未定義の動作が発生します。

class Base
{
public:
    virtual ~Base() {}
};
 
class Derived : public Base {};
 
Base* b = new Derived;
delete b; // safe

一般的なガイドラインとして、基底クラスのデストラクタはパブリックかつ仮想、またはプロテクテッドかつ非仮想のいずれかであるべきです。

[編集] 純粋仮想デストラクタ

候補(C++20以降) デストラクタは、純粋仮想として宣言されることがあります。これは、抽象化する必要があるが、純粋仮想として宣言できる他の適切な関数がない基底クラスなどで使用されます。純粋仮想デストラクタは定義を持つ必要があります。なぜなら、派生クラスが破棄される際にすべての基底クラスのデストラクタは常に呼び出されるからです。

class AbstractBase
{
public:
    virtual ~AbstractBase() = 0;
};
AbstractBase::~AbstractBase() {}
 
class Derived : public AbstractBase {};
 
// AbstractBase obj; // compiler error
Derived obj;         // OK

[編集] 例外

他の関数と同様に、デストラクタは例外をスローして終了することができます(これは通常、明示的にnoexcept(false)として宣言する必要があり(C++11以降)ます)。ただし、このデストラクタがスタック巻き戻し中に呼び出された場合、代わりにstd::terminateが呼び出されます。

std::uncaught_exceptionsはスタック巻き戻しが進行中であることを検出するために使用されることがありますが、デストラクタが例外をスローして終了することは一般的に悪い実践と見なされています。ただし、この機能は、一時オブジェクトのデストラクタが一時オブジェクトを構築した完全な式の終わりに例外をスローできる能力に依存しているSOCIGalera 3などの一部のライブラリで使用されています。

ライブラリ基本TS v3 のstd::experimental::scope_success は、例外を投げうるデストラクタを持つことがあり、スコープが正常に終了し、終了関数が例外をスローした場合に例外をスローします。

[編集] 注記

ローカル変数のような通常のオブジェクトに対してデストラクタを直接呼び出すと、スコープの終わりにデストラクタが再度呼び出されたときに未定義の動作が発生します。

ジェネリックなコンテキストでは、クラス型でないオブジェクトに対してデストラクタ呼び出し構文を使用できます。これは擬似デストラクタ呼び出しとして知られています。詳細はメンバアクセス演算子を参照してください。

機能テストマクロ 規格 機能
__cpp_trivial_union 202502L (C++26) 共用体の特殊メンバ関数の自明性要件の緩和

[編集]

#include <iostream>
 
struct A
{
    int i;
 
    A(int num) : i(num)
    {
        std::cout << "ctor a" << i << '\n';
    }
 
    (~A)() // but usually ~A()
    {
        std::cout << "dtor a" << i << '\n';
    }
};
 
A a0(0);
 
int main()
{
    A a1(1);
    A* p;
 
    { // nested scope
        A a2(2);
        p = new A(3);
    } // a2 out of scope
 
    delete p; // calls the destructor of a3
}

出力

ctor a0
ctor a1
ctor a2
ctor a3
dtor a2
dtor a3
dtor a1
dtor a0

[編集] 不具合報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 193 C++98 デストラクタ内の自動オブジェクトが
クラスの基底およびメンバサブオブジェクトの
破棄の前か後か
が未指定だった
それらのサブオブジェクトを
破棄する前に
破棄する C++98 CWG 344
デストラクタの宣言子構文が欠陥があった(CWG issue 194およびCWG issue 263と同じ問題)
構文を特殊な
関数宣言子構文に変更
CWG 1241 C++98 静的メンバがデストラクタ実行
直後に破棄される可能性があった
非静的メンバのみ
破棄する
CWG 1353 C++98 暗黙宣言されたデストラクタが
未定義となる条件が多次元配列型を考慮していなかった
これらの型を考慮する
CWG 1435 C++98 デストラクタの
宣言子構文における「クラス名」の意味が不明確だった
構文を特殊な
関数宣言子構文に変更
CWG 2180 C++98 最も派生したクラスではないクラスの
デストラクタが、その仮想直接基底クラスのデストラクタを呼び出す
それらのデストラクタは呼び出されない
CWG 2807 C++20 宣言指定子にconstevalを含めることができた 禁止された

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)