Help us understand the problem. What is going on with this article?

【c++】デストラクタにvirtualを付ける場合、付けない場合。

More than 5 years have passed since last update.

元ブログ、【c++】デストラクタにvirtualを付ける場合、付けない場合。 - 技術は熱いうちに打て!

virtual ~Hoge();

C++を書いていると良くvirtual宣言されているデストラクタを見ます。

まだまだC++初心者なので、なんでデストラクタにvirtualが付くのか分からず調べてみた結果をまとめてみたいと思います。
突っ込みあればお待ちしております。

まとめ

・ポリモーフィズムを利用すべく作った基底クラスのデストラクタはvirtualが必要である
=> でないと、派生クラスのデストラクタが呼ばれずメモリリークが起きる

・逆にポリモーフィズムを利用する意図がないのであれば派生クラスを作るつもりであってもvirtualなデストラクタにすべきではない
=> メモリの無駄遣いに繋がる

参考としたサイト・書籍

ビャーネ・ストラウストラップ (著) プログラミング言語C++第4版
C++では基底クラスにvirtualデストラクタを書こう
C++ でデストラクタを virtual にしなくてはならない条件と理由

では一つずつ見ていきましょう。

ポリモーフィズムを利用すべく作った基底クラスのデストラクタはvirtualが必要である

テストコードを見てみましょう。(C++では基底クラスにvirtualデストラクタを書こうより)

BadPractice.cpp
#include <stdio.h>

class BadBase {
  public:
    BadBase(){}
    ~BadBase(){}
};
class BadSub : public BadBase {
  public:
    BadSub(){ printf("open\n"); }
    ~BadSub() { printf("close\n"); }
};

int main() {
  BadSub* sub = new BadSub();
  delete sub;

  printf("\n");

  BadBase* base = new BadSub();
  delete base;

  return 0;
}

出力は以下の様になります。

open
close

open

ポリモーフィズムを用いている下部はcloseが呼ばれていない = デストラクタが呼ばれていません。

ではどうすれば良いかと言うともうお分かりかと思います。
virtualを付けてみましょう。

GoodPractice.cpp
#include <stdio.h>

class Base {
  public:
    Base(){}
    virtual ~Base(){}
};
class Sub : public Base {
  public:
    Sub(){ printf("open\n"); }
    ~Sub() { printf("close\n"); }
};

// mainは一緒

出力は以下です。

open
close

open
close

closeが呼び出されているのが分かります。

なぜこうなるのでしょうか。

ビャーネ・ストラウストラップ (著) プログラミング言語C++第4版にはこの様に書かれています。

17.2.3 基底とメンバのデストラクタ

コンストラクタは、クラスオブジェクトを"下から順に"構築する。
[1] まず、基底クラスのコンストラクタを実行する。
[2] 次に、メンバのコンストラクタを実行する。
[3] 最後に、自身の本体を実行する。

デストラクタは、逆の順序でオブジェクトを"破壊"していく。
[1] まず、自身の本体を実行する。
[2] 次に、メンバのデストラクタを実行する。
[3] 最後に、自身の本体を実行する。

悪い例の方では、親クラス(BadBase)のポインタに子クラス(BadSub)のポインタを代入しています。
デストラクタの[1]に書いてある"自身"とはこの例で言えばBadBaseの事になります。
なので、virtualがなければBadSubの存在に気付く事がなく子クラスのデストラクタが呼ばれないと言う仕組みなのでしょう。

逆にポリモーフィズムを利用する意図がないのであれば派生クラスを作るつもりであってもvirtualなデストラクタにすべきではない

C++ でデストラクタを virtual にしなくてはならない条件と理由によると、virtualを付ける事により
・インスタンスのサイズが大きくなる
・関数呼び出しが遅くなる
事が生じるので不用意に付けるべきではないと言う事です。

この辺はまだ勉強不足なので引き続き勉強を続けていきたいと思います。
今回はここまで。

誰かのお役に立てば。

ashdik
最近は、技術発信は自ブログでしています。 Twitterのフォローをお願いします。 個人でFlutter、キッズライン社でSwiftを主に書いています。
https://blog.dalt.me
kidsline
インターネットを使った女性支援事業、育児支援事業
https://kidsline.me/corp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away