CPPのvirtualデストラクタ
2. Kaleidoscope: Implementing a Parser and AST — LLVM 7 documentationでvirtual修飾子のついたデストラクタに出会った。いままで知らなかった(もしくは、忘れていた)ので、メモ。c++ - When to use virtual destructors? - Stack Overflowを参考にした。
#include <iostream> class Base { public: ~Base() { std::cout << "destructor at Base" << std::endl; } }; class Sub : public Base { ~Sub() { std::cout << "destructor at Sub" << std::endl; } }; int main() { Base *s = new Sub(); delete s; return 0; }
destructor at Base
一方、Baseのデストラクタにvirtual修飾子をつけることで、
#include <iostream> class Base { public: virtual ~Base() { std::cout << "destructor at Base" << std::endl; } }; class Sub : public Base { public: ~Sub() { std::cout << "destructor at Sub" << std::endl; } }; int main() { Base *s = new Sub(); delete s; return 0; }
destructor at Sub destructor at Base
とでき、デストラクターがサブクラスから順に呼ばれている。このように、オブジェクトを多相的に(ベースクラスオブジェクトとして)使いたいときは、ベースクラスのデストラクタにvirtual修飾子をつけておかないと、サブクラスのデストラクタが呼ばれない。
ちなみに、virtual修飾子がついた関数(=オーバーライドできる関数)を仮想関数と呼ぶらしい。ただ、デストラクターは特殊なオーバーライドになっている。普通のオーバーライドは次のように、
class Base { public: virtual void hoge() { std::cout << "hope at Base" << std::endl; } }; class Sub : public Base { public: void hoge() { std::cout << "hoge at Sub" << std::endl; } }; int main() { Base *s = new Sub(); s->hoge(); return 0; }
hoge at Sub
と、オーバーライドした関数のみが呼ばれるが、デストラクタの場合はオーバーライドしていると言うより、コールの連鎖を作っていると言った方が近い?