書籍転載
文法からはじめるプログラミング言語Microsoft Visual C++入門

C++のクラスをマスターしよう(前編)
―第10章 クラス〜オブジェクト指向プログラミング(前編)―

WINGSプロジェクト 矢吹 太朗(監修 山田 祥寛)
2010/05/19

10.1.7 デストラクタ

 デストラクタはオブジェクトを削除する際に呼ばれるメソッドですが、コンパイラが作るデストラクタは何の処理も行いません。オブジェクトを削除する際に特別な処理を行いたい場合には、自分でデストラクタを実装しなければなりません。

 デストラクタは次のように定義します(宣言と定義を分けることもできます)。デストラクタには引数や戻り値はありません。

~クラス名()
{
  文
}
[構文]デストラクタの定義

 削除の際にフィールドnameの値を表示するようなデストラクタを持つクラスPersonを使って、オブジェクトが削除されるようすを観察しましょう。

#include <iostream>
#include <string>
#include <memory>
using namespace std;
using namespace std::tr1;

class Person
{
  string name;
public:
  Person(string name) : name(name) {}

  //nameの値を表示するようなデストラクタ
   ~Person() { cout<<name<<"は削除された\n"; }
};

int main()
{
  Person a1("Taro"); //自動メモリに生成
  Person* pA2=new Person("Jiro"); //フリーストアに生成
  Person* pA3=new Person("Saburo"); //フリーストアに生成
  shared_ptr<Person> pA4(new Person("Shiro")); //フリーストアに生成

    delete pA2; //オブジェクトの削除
}
[サンプル]10-destructor1.cpp

 実行結果は以下のようになります。

Jiroは削除された
Shiroは削除された
Taroは削除された

 このコードでは以下の4つのオブジェクトを生成しています。

(1)Person a1("Taro");として自動メモリに生成したオブジェクトは、そのスコープが終了すると削除されます。

(2)new Person("Jiro")としてフリーストアに生成したオブジェクトは、それを指すポインタpA2をdeleteすることで削除されます。

(3)new Person("Saburo")としてフリーストアに生成したオブジェクトは、delete文がないため削除されません。この例では、すぐにプログラムが終了し、その際にプログラムが利用していたメモリはすべて解放されますが、プログラムが動き続ける場合には、このようなdelete文の書き忘れはメモリリークの原因になります。

(4)new Person("Shiro")としてフリーストアに生成したオブジェクトは、スマートポインタpA4で管理しているので、delete文を書かなくても削除されます(3.4.4項)。それぞれが生成されてから削除されるまでのようすを図示すると図10-3のようになります。

図10-3 4つのオブジェクトが生成されてから削除されるまでのようす

 デストラクタは再帰的に呼ばれます。つまり、クラスPersonのメンバとして別のクラスAがあるときに、Personオブジェクトを削除すれば、そのメンバであるAオブジェクトも削除されます。このことは、以下のコードで確認できます。

#include <iostream>
#include <string>
using namespace std;

struct A
{
  ~A() { cout<<"Aオブジェクトは削除された\n"; }
};

class Person
{
  string name;
  A a; //Aオブジェクトをメンバに持つ
public:
  Person(string name) : name(name) {}
  ~Person() { cout<<name<<"は削除された\n"; }
};

int main()
{
  Person a1("Taro");
  Person* pA2=new Person("Jiro");
  delete pA2;
}
[サンプル]10-destructor2.cpp

 プログラムの実行結果は次のようになります。

Jiroは削除された
Aオブジェクトは削除された
Taroは削除された
Aオブジェクトは削除された

 Aオブジェクトが2回削除されているので、a1のメンバと*pA2のメンバの両方が削除されていることがわかります。「A a;」のように生成されたオブジェクトは、それを保持するAオブジェクトが削除されれば削除されることがわかります。

10.1.8 デストラクタの役割

 これまで紹介したデストラクタは、すべてレポートを表示させるためだけのもので、特に重要な役割を果たしているわけではありません。デストラクタが重要になるのは、メンバがフリーストアに配置される場合です。

 次の例においては、Personオブジェクトが削除されるときに、ポインタpAが指すAオブジェクトをdelete pA;として削除しなければなりません。これを忘れると、Aオブジェクトがフリーストア内に残り、メモリリークの原因になります。delete pA;はPersonのデストラクタ内で実行します。このように、利用したオブジェクトを最後に削除するのがデストラクタの本来の役割です*5

*5 *pA を別の機会に削除することもできますが、慣れないうちは、それを生成したクラス(ここではPerson)のデストラクタで削除するようにしておいてください。

#include <iostream>
#include <string>
using namespace std;

struct A
{
  ~A() { cout<<"Aオブジェクトは削除された\n"; }
};

class Person
{
  string name;
  A* pA;
public:
  //Aオブジェクトをフリーストアに生成するコンストラクタ
  Person(string name) : name(name), pA(new A) {}

  //Aオブジェクトを削除するデストラクタ
  ~Person() {
    delete pA;
    cout<<name<<"は削除された\n";
  }
};

int main()
{
  Person a1("Taro");
  Person* pA2=new Person("Jiro");
  delete pA2;
}
[サンプル]10-destructor3.cpp

 実行結果は次のようになり、Aオブジェクトが削除されていることがわかります。

Aオブジェクトは削除された
Jiroは削除された
Aオブジェクトは削除された
Taroは削除された

 ~Person()の中にdelete pA;を書かなければ、Bオブジェクトは削除されないままフリーストアに残り、メモリリークの原因になります。

 オブジェクトの削除を常に意識するのが面倒な場合には、スマートポインタ(3.4.5項を参照)を利用します。~Person()の中にdelete文を書かなくても、Aオブジェクトは削除されます。

#include <iostream>
#include <string>
#include <memory>
using namespace std;
using namespace tr1;

struct A
{
  ~A() { cout<<"Aオブジェクトは削除された\n"; }
};

class Person
{
  string name;
  shared_ptr<A> pA; //スマートポインタを利用
public:
  Person(string name) : name(name), pA(new A) {}
  ~Person() { cout<<name<<" は削除された\n"; }
};

int main()
{
  Person a1("Taro");
  shared_ptr<Person> pA2(new Person("Jiro")); //スマートポインタを利用
}
[サンプル]10-destructor4.cpp

 このコードにはdelete文はありませんが、実行結果は次のようになり、すべてのオブジェクトを削除できていることがわかります。

Jiro は削除された
Aオブジェクトは削除された
Taro は削除された
Aオブジェクトは削除された


 INDEX
  [書籍転載]文法からはじめるプログラミング言語Microsoft Visual C++入門
  C++のクラスをマスターしよう(前編)
    1.クラスとは/クラスの生成
    2.メソッド/静的メンバ/アクセス制御
    3.コンストラクタ
  4.デストラクタ/デストラクタの役割
    5.リソース確保は初期化である

インデックス・ページヘ 「文法からはじめるプログラミング言語Microsoft Visual C++入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間