デストラクタがvirtualでないclassの継承には注意が必要という話を聞いたことがある人もいると思いますがその話をちょっとします
struct A { A() = default; ~A() { std::cout << "A destructor" << std::endl; } }; struct B:A { B() = default; ~B() { std::cout << "B destructor" << std::endl; } }; int main() { { std::unique_ptr<B> a = std::make_unique<B>(); } { std::unique_ptr<A> a = std::make_unique<B>(); } return 0; }
いまAのデストラクタはvirtualではありません。
このときの出力は
B destructor A destructor A destructor
になります。
非virtualデストラクタを持つ型からアップキャストした場合、破棄されるときに呼ばれるのは 基底classのデストラクタのみ になるので注意。
じゃあ基底classのデストラクタをvitualにすればいいじゃん?
確かにそうですがたとえば
std::vector
などのような標準ライブラリのclassなどで変更がきかない場合もあります。
でも、vectorを拡張したいとか思ったことありませんか?
アップキャストせずに使えばもちろん問題はないですがそれでも間違えてやってしまう場合もないとはいえません。
(派生先のデストラクタが何もしてないなら最悪呼ばれなくてもいいっちゃいいのかな?)
そういう時、より安全に使うためにはprotected継承してあげるのがよくある例だったりします。
struct A { A() = default; ~A() { std::cout << "A destructor" << std::endl; } }; struct B:protected A { B() = default; ~B() { std::cout << "B destructor" << std::endl; } }; int main() { { std::unique_ptr<B> a = std::make_unique<B>(); } { //std::unique_ptr<A> a = std::make_unique<B>(); これがコンパイルエラーになる } return 0; }
こうすることで、そもそもアップキャストして使うことそのものをエラーにできます。
ちなみに、Siv3Dのs3d::Array
もstd::vector
をprotected継承してます。
最後に、protected継承したということは、基底classでpublicだったものがすべてprotectedになるので 必要なものはusingしてあげるといいでしょう。
struct A { A() = default; ~A() { std::cout << "A destructor" << std::endl; } void hoge() {} }; struct B :protected A { B() = default; ~B() { std::cout << "B destructor" << std::endl; } using A::hoge; };