C++で基底クラスのデストラクタにvirtualを付ける理由

# この記事の概要
C++で基底クラスのデストラクタにvirtualを付けることの意味を理解していなかったので、実験してみて理解したことを書き残します。

# 結論
派生クラスのインスタンスをnewして、基底クラスのポインタで指す場合、基底クラスのポインタから派生クラスのインスタンスを完全にdeleteするために必要。

# 実験
こちらの記事をググってみつけて、真似して実験してみました。
https://qiita.com/ryuj/items/ad975307cd303e3bf590

## 実験環境
* Windows10professional
* VisualStudio2019

## 実験の流れ
基底クラスをB、派生クラスをDとします。コンストラクタ、デストラクタでメッセージを表示します。インスタンスの識別ができるように、コンストラクタの引数で番号を付けられるようにしました。
これらのクラスをnewする際、deleteする際に、コンストラクタ、デストラクタがどのように呼び出されるかを確認します。

## 実験その1
まずは、基底クラスのデストラクタに修飾子を付けないパターンでやってみます。

クラスの定義、実装はこの通り。

```C++
#include<iostream>
//===================================================
class B {
public:
	B(int n=0){
		n_=n;
		std::cout<<"constructor in B"<<n_<<std::endl;
	}
	~B(){
		std::cout<<"destructor in B"<<n_<<std::endl;
	}
private:
	int n_;
};///////////////////////////////////////////////////
//===================================================
class D : public B {
public:
	D(int n=0):B(n){
		n_=n;
		std::cout<<"constructor in D"<<n_<<std::endl;
	}
	~D(){
		std::cout<<"destructor in D"<<n_<<std::endl;
	}
private:
	int n_;
};///////////////////////////////////////////////////
```
では実験開始。
newとdeleteをこんな感じでやってみます。

```C++
//===================================================
int main(int argc, char* argv[])
{
	std::cout << "Bのインスタンスを、B型ポインタで指す"<< std::endl;
	B* b1;
	b1 = new B(1);
	std::cout<<"..."<<std::endl;
	delete b1;
	std::cout<<"-----"<<std::endl;

	std::cout << "Dのインスタンスを、D型ポインタで指す" << std::endl;
	D* d2;
	d2 = new D(2);
	std::cout<<"..."<<std::endl;
	delete d2;
	std::cout<<"-----"<<std::endl;

	std::cout << "Dのインスタンスを、B型ポインタで指す" << std::endl;
	B* b3;
	b3 = new D(3);
	std::cout<<"..."<<std::endl;
	delete b3;
	std::cout << "-----" << std::endl;

	return 0;
}////////////////////////////////////////////////////
```

実行結果は・・・

```
Bのインスタンスを、B型ポインタで指す
constructor in B1
...
destructor in B1
-----
Dのインスタンスを、D型ポインタで指す
constructor in B2
constructor in D2
...
destructor in D2
destructor in B2
-----
Dのインスタンスを、B型ポインタで指す
constructor in B3
constructor in D3
...
destructor in B3
-----
```

「Bのインスタンスを、B型ポインタで指す」場合は、newしたらBのコンストラクタが呼ばれて、deleteしたらBのデストラクタが呼ばれます。想定通り。
「Dのインスタンスを、D型ポインタで指す」場合は、newしたらBのコンストラクタとDのコンストラクタが呼ばれて、deleteしたらDのデストラクタとBのデストラクタが呼ばれます。派生クラスなので基底クラスのコンストラクタ、デストラクタも呼ばれるわけです。
次が少し変則的。「Dのインスタンスを、B型ポインタで指す」場合です。newしたらBのコンストラクタとDのコンストラクタが呼ばれます。これは直感的に納得。でもdeleteしたら、Bのデストラクタだけが呼ばれました。Dのデストラクタは呼ばれていないから、DのインスタンスのB以外の部分が残ってしまった？Dのインスタンス部分も消したいのに。よくよく考えると、B型のポインタが指すのはBのデストラクタである、というこれまた直感的な解釈で一応納得しました。

## 実験その2
次に、基底クラスのデストラクタにvirtual修飾子を付けたパターンでやってみました。

クラスの定義、実装はこの通り。

```C++
#include<iostream>
//===================================================
class B {
public:
	B(int n=0){
		n_=n;
		std::cout<<"constructor in B"<<n_<<std::endl;
	}
	virtual ~B(){
		std::cout<<"destructor in B"<<n_<<std::endl;
	}
private:
	int n_;
};///////////////////////////////////////////////////

// 以下は実験1と同様なので省略
```
で、実験開始。mainのルーチンは実験1と同じなので省略します。
実行結果は・・・

```
Bのインスタンスを、B型ポインタで指す
constructor in B1
...
destructor in B1
-----
Dのインスタンスを、D型ポインタで指す
constructor in B2
constructor in D2
...
destructor in D2
destructor in B2
-----
Dのインスタンスを、B型ポインタで指す
constructor in B3
constructor in D3
...
destructor in D3
destructor in B3
-----
```
今度は「Dのインスタンスを、B型ポインタで指す」で、deleteしたら、DのデストラクタとBのデストラクタが呼ばれました。これで綺麗サッパリ削除できました。

ということで、「派生クラスのインスタンスをnewして、基底クラスのポインタで指す場合、基底クラスのポインタから派生クラスのインスタンスを完全にdeleteするために必要。」ということが理解できました。

# あとがき
virtual修飾子の使い方、意味をはっきりと理解せずにコーディングをしていたことが今回、解りました。ガチの製品コーディングだったら怒られますね。