「継承と仮想デストラクタ」の版間の差分

提供: C++入門
移動: 案内検索
(ページの作成:「<!-- vim: filetype=mediawiki --> 読み方 __TOC__ == 概要 == 継承とコンストラクタとデストラクタで[[コンストラクタとデストラク...」)
 
 
行1: 行1:
<!--
+
[[クラス]]を継承するときに、[[基底クラス]]が仮想デストラクタ([[デストラクタ]]が[[仮想関数]]ではない)を持たない場合、[[継承]]してはいけません。
vim: filetype=mediawiki
+
-->
+
 
+
読み方
+
  
 +
'''読み方'''
 +
;仮想デストラクタ:かそう ですとらくた
 +
;継承:けいしょう
 
__TOC__
 
__TOC__
  
 
== 概要 ==
 
== 概要 ==
 
 
[[継承とコンストラクタとデストラクタ]]で[[コンストラクタとデストラクタ]]が呼び出される順番について書きました。
 
[[継承とコンストラクタとデストラクタ]]で[[コンストラクタとデストラクタ]]が呼び出される順番について書きました。
  
行154: 行152:
  
 
== 仮想デストラクタが持たないクラスを継承してはいけない ==
 
== 仮想デストラクタが持たないクラスを継承してはいけない ==
 
 
どんな[[クラス]]でもデストラクタを仮想デストラクタ(virtualデストラクタ)にしないといけない、というわけではありません。
 
どんな[[クラス]]でもデストラクタを仮想デストラクタ(virtualデストラクタ)にしないといけない、というわけではありません。
  
行163: 行160:
  
 
== 関連項目 ==
 
== 関連項目 ==
 
 
* [[継承]]
 
* [[継承]]
 
* [[仮想関数]]
 
* [[仮想関数]]
 
* [[コンストラクタとデストラクタ]]
 
* [[コンストラクタとデストラクタ]]
 +
<!-- vim: filetype=mediawiki
 +
-->

2014年7月27日 (日) 15:13時点における最新版

クラスを継承するときに、基底クラスが仮想デストラクタ(デストラクタ仮想関数ではない)を持たない場合、継承してはいけません。

読み方

仮想デストラクタ
かそう ですとらくた
継承
けいしょう

概要

継承とコンストラクタとデストラクタコンストラクタとデストラクタが呼び出される順番について書きました。

仮想関数でも書きましたが、サブクラスのインスタンスを基底クラスのポインタで扱ったときに、デストラクタの呼び出しに特徴があります。 デストラクタの書き方について、このページで補足します。

サブクラスのデストラクタが呼び出されない例

ソースコード deprecation_extends_1.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void x() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B *p = new C1();
 
        p->f();
 
        delete p;
 
        return 0;
}

コンパイル

g++  deprecation_extends_1.cpp -o deprecation_extends_1

実行例

C1のインスタンスを基底クラス B のポインタで受け取って、解放した場合、基底クラスのデストラクタは呼び出されていますが、サブクラス C1 のデストラクタが呼び出されませんでした。 もし、サブクラスの中でリソースの解放などの必須処理が記述されている場合、実行されなくなり、システムへ影響を与える問題に発展します。

% ./deprecation_extends_1
B::B()
C1::C1()
void B::f()
B::~B()

次の項目で、サブクラスのデストラクタが呼び出されない問題を解決します。

サブクラスのデストラクタの問題を解決する例

deprecation_extends_1.cpp の例との違いは、基底クラス のデストラクタに virtual が指定されていることです。 デストラクタに virtual を指定することにより、インスタンスが解放されるときに、基底クラスとサブクラスの両方のデストラクタが呼び出されます。

ソースコード virtual_destructor_2.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                virtual ~B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void x() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B *p = new C1();
 
        p->f();
 
        delete p;
 
        return 0;
}

コンパイル

g++  virtual_destructor_2.cpp -o virtual_destructor_2

実行例

基底クラスとサブクラスの両方のデストラクタが呼び出されるようになりました。

% ./virtual_destructor_2
B::B()
C1::C1()
void B::f()
virtual C1::~C1()
virtual B::~B()

仮想デストラクタが持たないクラスを継承してはいけない

どんなクラスでもデストラクタを仮想デストラクタ(virtualデストラクタ)にしないといけない、というわけではありません。

クラスがvirtualをもった場合、仮想関数テーブルが作成されます。仮想関数テーブルを持つとオブジェクトサイズが大きくなってしまいます。

関連項目