結論から言うと、保証されていると考えてよいと思います。
デストラクタに virtual
を付与した派生クラスからデストラクタに関する仮想関数テーブルが作られます。
仮想関数テーブルは派生クラスにも引き継がれます。 派生クラスのデストラクタに virtual
をつけなくてもvirtual
扱いになります。
参考:
https://teratail.com/questions/26699
また、デストラクタが呼ばれる順番は末端の派生先クラスから順に呼ばれていきます。
試しに、派生クラスを増やしてそれぞれのクラスからdeleteしてみます。
cpp
1#include <iostream>
2struct cl0 {
3 ~cl0() {
4 std::cout << "~cl0" << std::endl;
5 }
6};
7struct cl1 : cl0{
8 ~cl1() {
9 std::cout << "~cl1" << std::endl;
10 }
11};
12struct cl2 : cl1 {
13 virtual ~cl2() {
14 std::cout << "~cl2" << std::endl;
15 }
16};
17struct cl3 : cl2 {
18 ~cl3() {
19 std::cout << "~cl3" << std::endl;
20 }
21};
22struct cl4 : cl3 {
23 ~cl4() {
24 std::cout << "~cl4" << std::endl;
25 }
26};
27int main() {
28 std::cout << "cl1 destractor" << std::endl;
29 cl1 *p1 = new cl4;
30 delete p1;
31 std::cout << "cl2 destractor" << std::endl;
32 cl2 *p2 = new cl4;
33 delete p2;
34 std::cout << "cl3 destractor" << std::endl;
35 cl3 *p3 = new cl4;
36 delete p3;
37 std::cout << "cl4 destractor" << std::endl;
38 cl4 *p4 = new cl4;
39 delete p4;
40}
実行結果
cl1 destractor
~cl1
~cl0
cl2 destractor
~cl4
~cl3
~cl2
~cl1
~cl0
cl3 destractor
~cl4
~cl3
~cl2
~cl1
~cl0
cl4 destractor
~cl4
~cl3
~cl2
~cl1
~cl0
virtual
がついているのはcl2からです。cl1クラスの変数に入れたインスタンスをdeleteする場合はcl1のデストラクタから呼ばれます。cl1には仮想関数テーブルがないのでcl1とその派生元のcl0しかデストラクタが呼ばれません。
対して、cl2からはデストラクタが仮想関数テーブルに入っています。cl3以降も virtual
キーワードがついていませんが仮想関数テーブルにデストラクタが追加で入ります。cl4にはcl2,cl3,cl4のデストラクタが入っていることになります。
virtualなデストラクタを持つインスタンスがdeleteされる際は、変数の型のデストラクタではなくインスタンスの型のデストラクタが最初に呼び出されます。そのまま継承順の逆順にデストラクタが呼ばれます。cl2のデストラクタが呼ばれた際は、virtualがついていない場合と同じく基底クラスcl1のデストラクタが呼ばれます。cl1のデストラクタが呼ばれた後、virtualがついていない場合のルールにのっとりcl0のデストラクタが呼ばれます。
以上により、virtualデストラクタとなったクラスから先はインスタンスの型でデストラクタが呼ばれます。
virtualをつけるとクラスオブジェクトのサイズが大きくなるので、必要な派生先からvirtualを付けるなどの工夫が必要になると思われます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/07/31 16:47