継承するなら、基本・派生どちらにもデストラクタを書かないとしても、仮想デストラクタが必要!?

Pocket

継承されるクラスに仮想デストラクタを書かなくてはならない理由として、
よく、ポリモーフィズムを使った場合に、派生クラスのデストラクタが実行されない理由が挙げられる。
“継承 仮想デストラクタ” でググった結果

そうすると、「派生クラスにデストラクタ書かなければ当然仮想デストラクタでなくてもいい」と思いがちだが、
実はそんなことは全然無い。

派生クラスが他のクラスを包含していると、問題が起きる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include <iostream>

struct tagObj {
    tagObj() {
        std::cout << "new!" << std::endl;
    }
    ~tagObj(){
        std::cout << "delete!" << std::endl;
    }
};

struct tagBase {
    virtual ~tagBase(){}
};

struct tagDrvt : tagBase {
    tagObj o;
};

int main(){
    tagBase *pB = new tagDrvt();
    delete pB;
}
new!
delete!

この場合意図したとおりに、包含されているクラスのコンストラクタとデストラクタが呼ばれている。

さて、ここで基本クラスの仮想デストラクタを無くした場合どうなるか。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include <iostream>

struct tagObj {
    tagObj() {
        std::cout << "new!" << std::endl;
    }
    ~tagObj(){
        std::cout << "delete!" << std::endl;
    }
};

struct tagBase {
    //virtual ~tagBase(){}  // ←仮想デストラクタなし
};

struct tagDrvt : tagBase {
    tagObj o;
};

int main(){
    tagBase *pB = new tagDrvt();
    delete pB;
}
new!

13行目の空の仮想デストラクタを無くしただけだが、
このように、派生クラスが包含しているクラスのデストラクタが呼ばれなくなる。
(ここではtagObj o;のこと)

理由は「派生デストラクタが呼ばれない」と言う点で、
ポリモーフィズムを使った場合に、派生クラスのデストラクタが実行されない、というのと全く同じ。

しかし、派生クラスにデストラクタが書かれていないのでややこしい。
書かれていなくても、コンパイラが自動で包含するクラスを破棄するデストラクタを生成しているので、
基本クラスのデストラクタが仮想関数でないといけないのだ。

 

ちなみにboost::shared_ptrとかを使えば、
テンプレートを使って、セットされたポインタの型を覚えてくれるので、
shared_ptrを基本型で作成しても、しっかり派生型のデストラクタを呼び出してくれる。
(std::autoptrとかではダメ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <boost/shared_ptr.hpp>

struct tagObj {
    tagObj() {
        std::cout << "new!" << std::endl;
    }
    ~tagObj(){
        std::cout << "delete!" << std::endl;
    }
};

struct tagBase {
    //virtual ~tagBase(){}  // ←仮想デストラクタなし
};

struct tagDrvt : tagBase {
    tagObj o;
};

int main(){
    // 基本型のスマートポインタに派生型をセットする
    boost::shared_ptr<tagBase> pB(new tagDrvt());
    boost::shared_ptr<tagBase> pB2;
    pB2.reset(new tagDrvt());
}
new!
new!
delete!
delete!

コメントを残す

メールアドレスが公開されることはありません。