Namespaces
Variants

Destructors

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

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

(C++20以降)

目次

構文

デストラクタ (C++20まで) プロスペクティブデストラクタ (C++20以降) は以下の形式のメンバー 関数宣言子 を使用して宣言されます:

class-name-with-tilde ( parameter-list  (オプション) ) except  (オプション) attr  (オプション)
class-name-with-tilde - 識別子式、 属性のリストが続く可能性があり、 (C++11以降) 括弧の組で囲まれる可能性がある
parameter-list - パラメータリスト (空または void でなければならない)
except -

動的例外指定

(C++11まで)

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

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

noexcept指定

(C++17以降)
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以降) となる。
  • それ以外の場合、識別子式は、修飾識別子の非終端部分によって指名されるクラスの注入クラス名の前に ~ が付いた終端非修飾識別子を持つ修飾識別子である。

説明

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

  • スレッドローカルストレージ期間を持つオブジェクトのスレッド終了時
(C++11以降)
  • スコープの終了時、自動記憶域期間を持つオブジェクトおよび参照にバインドすることで寿命が延長された一時オブジェクトに対して
  • delete 、動的記憶域期間を持つオブジェクトに対して
  • 完全な の終了時、名前のない一時オブジェクトに対して
  • スタックアンワインディング 、例外がブロックから抜けて捕捉されない場合の自動記憶域期間を持つオブジェクトに対して

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

プロスペクティブデストラクタ

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

どのプロスペクティブデストラクタがデストラクタであるかを決定するために、クラス定義の終了時に、 オーバーロード解決 が空の引数リストでクラス内で宣言されたプロスペクティブデストラクタ間で実行されます。オーバーロード解決が失敗した場合、プログラムは不適格となります。デストラクタの選択は選択されたデストラクタを odr-use せず、選択されたデストラクタは削除されている可能性があります。

すべてのプロスペクティブデストラクタは特殊メンバ関数です。クラス T に対してユーザー宣言されたプロスペクティブデストラクタが提供されない場合、コンパイラは常に 暗黙的に宣言 し、暗黙的に宣言されたプロスペクティブデストラクタは 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 メンバーとして宣言します。

他の暗黙的に宣言される特別なメンバ関数と同様に、暗黙的に宣言されるデストラクタの例外仕様は、非スロー(non-throwing)です。ただし、 構築される可能性のある基底クラスまたはメンバのデストラクタが 潜在的にスローする可能性がある場合 (C++17以降) 暗黙的な定義が異なる例外仕様を持つ関数を直接呼び出す場合 (C++17以前) を除きます。実際には、暗黙的なデストラクタは noexcept であり、デストラクタが noexcept ( false ) である基底クラスまたはメンバによって「汚染」されたクラスでない限り、その限りではありません。

暗黙的に定義されるデストラクタ

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

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

(C++20以降)


削除されたデストラクタ

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

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

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

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

自明なデストラクタ

T クラスのデストラクタは、以下のすべての条件を満たす場合にトリビアルとなります:

  • デストラクタは 暗黙的に宣言される (C++11まで) ユーザー提供ではない user-provided (C++11以降)
  • デストラクタは仮想ではない。
  • すべての直接基底クラスは自明なデストラクタを持つ。
  • クラス型(またはクラス型の配列)のすべての非静的データメンバがトリビアルなデストラクタを持つ。
(C++26まで)
  • T が共用体であるか、またはクラス型(またはクラス型の配列)のすべての非バリアント非静的データメンバがトリビアルなデストラクタを持つ。
(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) 共用体の特殊メンバー関数に対する自明性要件の緩和

#include <iostream>
struct A
{
    int i;
    A(int num) : i(num)
    {
        std::cout << "ctor a" << i << '\n';
    }
    (~A)() // ただし通常は ~A()
    {
        std::cout << "dtor a" << i << '\n';
    }
};
A a0(0);
int main()
{
    A a1(1);
    A* p;
    { // ネストされたスコープ
        A a2(2);
        p = new A(3);
    } // a2がスコープ外に
    delete p; // a3のデストラクタを呼び出す
}

出力:

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 を含むことができた 禁止

関連項目