名前空間
変種
操作

デストラクタ

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
コピー代入
ムーブ代入 (C++11)
デストラクタ
テンプレート
その他
 
 

デストラクタはオブジェクトの生存期間が終了したときに呼ばれる特別なメン関数です。 デストラクタの目的はオブジェクトがその生存期間中に取得したリソースを解放することです。

目次

[編集] 構文

~ class_name (); (1)
virtual ~ class_name (); (2)
decl-specifier-seq(オプション) ~ class_name () = default; (3) (C++11以上)
decl-specifier-seq(オプション) ~ class_name () = delete; (4) (C++11以上)
attr(オプション) decl-specifier-seq(オプション) id-expression ( void(オプション) ) except(オプション) attr(オプション) ; (5)
1) 一般的なデストラクタの宣言。
2) 仮想デストラクタは通常、基底クラスにおいて要求されます。
3) デストラクタを強制的にコンパイラに生成させます。
4) 暗黙のデストラクタを無効化します。
5) デストラクタの宣言の正式な構文。
decl-specifier-seq - friendinlinevirtual または何もなし (戻り値の型は指定できません)。
id-expression - クラス定義内では、シンボル ~ の後に class_name が続いたもの。 クラステンプレート内では、シンボル ~ の後にテンプレートの現在の実体化の名前が続いたもの。 名前空間スコープまたは異なるクラス内の friend 宣言では、 nested-name-specifier の後にシンボル ~ が続いたもののの後に nested-name-specifier によって表されるものと同じクラスの class_name が続いたもの。 いずれの場合でも、名前は typedef ではなくクラスまたはテンプレートの実際の名前でなければなりません。 id 式の全体を括弧で囲っても良く、その意味は変わりません。
attr(C++11) - オプショナルな任意個の属性の並び。
except - あらゆる関数宣言の場合と同様の例外指定 (動的例外指定(非推奨)(C++17で削除)または noexcept 指定子(C++11)のいずれか)。

ただし、例外指定が明示的に提供されない場合、例外指定は暗黙に宣言されたデストラクタ (後述) で用いられるあろうものであるとみなされます。 ほとんどの場合、それは noexcept(true) です。 そのため例外を投げるデストラクタは明示的に noexcept(false) 宣言されなければなりません。

(C++11以上)


[編集] 説明

デストラクタはオブジェクトの生存期間が終了したときに呼ばれます。 これには以下が含まれます。

  • スレッドの終了 (スレッドローカル記憶域期間を持つオブジェクトの場合)。
(C++11以上)
  • スコープの終了 (自動記憶域期間を持つオブジェクトおよび参照への束縛によって生存が延長された一時オブジェクトの場合)。
  • delete 式 (動的記憶域期間を持つオブジェクトの場合)。
  • 完全の終了 (名前のない一時オブジェクトの場合)。
  • スタックの巻き戻し (例外がキャッチされずにそのブロックを脱出したときの自動記憶域期間を持つオブジェクトの場合)。

デストラクタは、例えば配置 new を用いて構築されたオブジェクトを破棄するために、直接呼ぶこともできます。 また、アロケータを通して構築されたオブジェクトを破棄するために、 std::allocator::destroy() のようなアロケータのメンバ関数を通して呼ぶこともできます。 ローカル変数のような普通のオブジェクトに対してデストラクタを直接呼ぶと、そのデストラクタがそのスコープの終わりで再度呼ばれたとき、未定義動作を発生させることに注意してください。

総称の文脈では、デストラクタ呼び出しの構文を非クラス型のオブジェクトに対して使用できます。 これは疑似デストラクタ呼び出しと言います。 メンバアクセス演算子を参照してください。

[編集] 暗黙に宣言されたデストラクタ

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

あらゆる暗黙に宣言された特別なメンバ関数と同様に、暗黙に宣言されたデストラクタの例外指定は、いずれかの潜在的に構築される基底またはメンバのデストラクタが潜在的に例外を投げる (C++17以上)暗黙の定義が異なる例外指定を持つ関数を直接呼ぶ (C++17未満)場合を除いて、例外を投げません。 実際のところ、暗黙のデストラクタは、 noexcept(false) なデストラクタを持つ基底またはメンバによってそのクラスが「汚染」されない限り、 noexcept です。

[編集] 削除された暗黙に宣言されたデストラクタ

クラス T に対する暗黙に宣言されたまたはデフォルト化されたデストラクタは、以下のいずれかが真の場合、未定義です (C++11未満)削除されたものとして定義されます (C++11以上)

  • T が破棄できない (削除されたまたはアクセス不可能なデストラクタを持つ) 非静的データメンバを持つ。
  • T が破棄できない (削除されたまたはアクセス不可能なデストラクタを持つ) 直接または仮想の基底クラスを持つ。
  • T が共用体であり、非トリビアルなデストラクタを持つ変種メンバを持つ。
(C++11以上)
  • 暗黙に宣言されたデストラクタが (基底クラスが仮想デストラクタを持つために) 仮想であり、破棄関数 (operator delete()) の名前探索の結果が曖昧な、削除された、またはアクセス不可能な関数の呼び出しである。

[編集] トリビアルなデストラクタ

以下のすべてが真の場合、クラス T に対するデストラクタはトリビアルです。

  • デストラクタがユーザ提供されない (つまり、暗黙に宣言されたか、その最初の宣言において明示的にデフォルト化されたものとして定義されたかの、いずれか)。
  • デストラクタが仮想でない (つまり、基底クラスのデストラクタが仮想でない)。
  • すべての直接の基底クラスがトリビアルなデストラクタを持つ。
  • クラス型 (またはクラスの配列型) のすべての非静的データメンバがトリビアルなデストラクタを持つ。

トリビアルなデストラクタは、何も行わないデストラクタです。 トリビアルなデストラクタを持つオブジェクトは delete 式を要求せず、その記憶域を単に解放することによって処分できます。 C 言語と互換性のあるすべてのデータ型 (POD 型) はトリビアルに破棄可能です。

[編集] 暗黙に定義されたデストラクタ

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

[編集] 破棄シーケンス

ユーザ定義されたまたは暗黙に定義されたデストラクタの両方について、デストラクタの本体が実行された後、コンパイラはそのクラスのすべての非静的非変種メンバのデストラクタを宣言の逆順で呼び、その後、すべての直接の非仮想基底クラスのデストラクタを構築の逆順で呼び (それはさらにそのメンバおよび基底クラスのデストラクタを呼び、以下同様)、その後、このオブジェクトが最も派生したクラスであれば、すべての仮想基底のデストラクタを呼びます。

デストラクタが直接呼ばれる (例えば obj.~Foo();) ときでも、 ~Foo() 内の return 文は直ちには制御を呼び出し元へ戻しません。 そのすべてのメンバと基底のデストラクタを最初に呼びます。

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

基底へのポインタを通したオブジェクトの削除は、その基底クラスのデストラクタが仮想でなければ、未定義動作を発生させます。

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

基底クラスに対するデストラクタはパブリックかつ仮想またはプロテクデットかつ非仮想のいずれかでなければならない、というのが一般的なガイドラインです。

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

デストラクタは純粋仮想として宣言しても構いません。 例えば、抽象にする必要があるけれども純粋仮想として宣言できる適当な関数が他にない基底クラスの場合などです。 派生クラスが破棄されるときはすべての基底クラスのデストラクタが必ず呼ばれるため、そのようなデストラクタは定義を持たなければなりません。

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_exception が使われることがありますが、例外を投げることによる終了を任意のデストラクタに認めるのは一般的には悪い習慣であると考えられています。 とはいっても、 SOCIGalera 3 などのいくつかのライブラリは、完全式の終了時に例外を投げるために名前のない一時オブジェクトのデストラクタの能力に依存しており、この機能を使用しています。

[編集]

#include <iostream>
 
struct A
{
    int i;
 
    A ( int i ) : i ( i ) 
    {
        std::cout << "ctor a" << i << '\n';
    }
 
    ~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