質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

10273閲覧

親の仮想デストラクタをoverrideした子のインスタンスから親のデストラクタが呼ばれる

nemumitakamaru

総合スコア22

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/11/23 15:20

親の仮想関数をoverrideした場合、下記のソースコードのChild::Print()内のParent::Print()のような感じでoverrideした子の関数から明示的に呼び出されない限りは親の関数は呼び出されないと思っていました。

しかし、下記のソースコードを実行してみると、親の仮想デストラクタをoverrideした子のインスタンスの破棄時に親のデストラクタが呼び出されていました。

仮想デストラクタのみ特別な仕様となっているのでしょうか?

ソースコード

cpp

1#include <iostream> 2 3using namespace std; 4 5class Parent { 6 7public: 8 virtual ~Parent(){ 9 cout << "Parent ::~Parent()" << endl; 10 } 11 12 virtual void Print() { 13 cout << "Parent ::Print()" << endl; 14 } 15}; 16 17class Child : public Parent { 18public: 19 virtual ~Child() override { 20 cout << "Child::~Child()" << endl; 21 } 22 23 virtual void Print() override { 24 cout << "Child::Print()" << endl; 25 26 Parent::Print(); 27 } 28}; 29 30int main() { 31 Child child; 32 child.Print(); 33 cout << endl; 34 35 return 0; 36}

実行結果

Child::Print() Parent ::Print() Child::~Child() Parent ::~Parent()

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

デストラクタとオーバーライドがごっちゃになってるのでは?
分けて考えれば特別扱いはしていません。

デストラクタの仕様として、派生クラス(子)のデストラクタは抜ける直前に暗黙的に基底クラス(親)のデストラクタを呼び出します。

C++

1class Child : public Parent 2{ 3public: 4 ~Child() 5 { 6 // Childの破棄 7 8 Parent::~Parent(); // 自動的に呼び出される 9 } 10}; 11

これはデストラクタが仮想関数であるとかオーバーライドしているかは関係なくデストラクタの仕様です。

これとは別に、仮想関数をオーバーライドした場合、基底クラスのものに対する呼び出しを派生クラスによって上書きしたようになりますが、あくまで呼び出しを上書きしただけでChild::Printのように基底クラスのオーバーライドした関数を呼ぶのは自由です。

ですのでデストラクタをオーバーライドしても、デストラクタの"呼び出し"が上書きされるだけで、基底クラスのデストラクタを暗黙的に呼び出すのはデストラクタの仕様のままですから、実行結果の通りになります。

投稿2018/11/23 18:38

toki_td

総合スコア2850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nemumitakamaru

2018/11/24 14:38

ご回答ありがとうございます。 良く理解できました。 おっしゃる通り、色々と考えがごっちゃになっていました。
guest

0

cppreference.com - virtual function specifierには、次のようなサンプルがあります。

C++

1class Base { 2 public: 3 virtual ~Base() { /* releases Base's resources */ } 4};

class Derived : public Base {
~Derived() { /* releases Derived's resources */ }
};

int main()
{
Base* b = new Derived;
delete b; // Makes a virtual function call to Base::~Base()
// since it is virtual, it calls Derived::~Derived() which can
// release resources of the derived class, and then calls
// Base::~Base() following the usual order of destruction
}

基底クラスの領域を解放するために、デストラクタを呼んでいるようです。


もうちょっと直接的な記述が無いのか規格を開いてみたのですが、見つけられませんでした。 ※
非公式なもので良ければ、次のような説明はあります。

派生クラスのコンストラクタを呼ぶ前に基底クラスのコンストラクタが呼ばれ、派生クラスのデストラクタが呼ばれた後に基底クラスのデストラクタが呼ばれるのです。

引用元: ロベールのC++教室 - 第16章 派生と構築 -

※ 私の調査不足でした。asmさんとyumetodoさんのコメントをご覧ください。


不正確なことを書いてしまったようですので、以下の回答は撤回します。

残骸

ポインタにしないとメンバを動的に決定してくれません。

C++

1int main() { 2 Parent* obj = new Child; 3 obj->Print(); 4 cout << endl; 5 6 delete obj; 7 return 0; 8}

メモリリークを回避するために、スマートポインタを使うとさらに良いでしょう。

C++

1int main() { 2 auto obj = std::make_unique<Child>(); 3 obj->Print(); 4 cout << endl; 5 6 return 0; 7}

投稿2018/11/23 15:24

編集2018/11/23 23:51
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asm

2018/11/23 17:24 編集

https://timsong-cpp.github.io/cppwp/n3337/class.dtor#8 かな 英語力が足りず理解が微妙だが > if X is the type of the most derived class ([class.base.init]), its destructor calls the destructors for X's virtual base classes. あたりがそれっぽい
yumetodo

2018/11/23 18:38

virtual base classesではないのでそこじゃなくてその一つ前の >destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes 直接の基底クラスというのが該当じゃないでしょうか
LouiS0616

2018/11/23 23:49

なるほど、ご教示ありがとうございます。 回答に反映させていただきます。
asm

2018/11/23 23:54

@yumetodo ありがとうございます。 なるほど、デストラクタが直接の基底クラスのデストラクタを呼ぶのが規定されているので virtualにすると派生クラスのデストラクタから、その直接の基底クラスのデストラクタが呼ばれていくのですね。
yumetodo

2018/11/24 01:30

@asm いえ、この場合virtualは関係ないです。@toki_td氏の解釈が正しいと思います。
asm

2018/11/24 01:33

言葉が足りませんでしたね 基底クラスのポインタをdelete時に派生クラスのデストラクタが呼ばれる事を指したつもりでした。
yumetodo

2018/11/24 03:18

ああ、なるほど。
nemumitakamaru

2018/11/24 14:42

皆様、ご回答ありがとうございます。 規格書での記載箇所まで教えていただき、勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問