C++ の継承でよく見かける virtual について
何度も調べているので,もう自分でまとめた.
参考資料
http://www.yunabe.jp/docs/cpp_virtual_destructor.html
virtual
に関する実験
#include "bits/stdc++.h" using namespace std; struct A{ int x; A(int _x = 1):x(_x){ cout << "construct A : " << x << endl; } virtual ~A(){ cout << "destruct A : " << x << endl; } virtual void func(){ cout << "func A : x=" << x << endl; } }; struct B: public A{ int y; B(int _y = 2, int _x = 1):A(_x),y(_y){ cout << "construct B : " << x << ',' << y << endl; } ~B(){ cout << "destruct B : " << x << ',' << y << endl; } void func(){ cout << "func B : y=" << y << endl; } }; int main(){ unique_ptr<A> ptr; ptr.reset(new A(10)); ptr->func(); ptr.reset(new B(20, 30)); ptr->func(); A* p = new B(9, 99); p->func(); delete p; return 0; }
結果.
construct A : 10 func A : x=10 construct A : 30 construct B : 30,20 destruct A : 10 func B : y=20 construct A : 99 construct B : 99,9 func B : y=9 destruct B : 99,9 destruct A : 99 destruct B : 30,20 destruct A : 30
A*
に B*
を代入しても,B
側のメソッドが呼ばれる.デストラクタも同様.
かなり不思議な挙動だと思いませんか?
変数 A* p
の気持ちになって観察すると,A
のポインタが入っているはずなので,p->func()
は A::func()
を呼び出すはず.
仕掛け
仮想関数テーブル (vtable) と呼ばれる機構があり,メソッドに virtual
と修飾するだけで,適切な関数が呼び出されるよう「動的に」解決してくれる.
動的に解決する以上,ある程度のオーバーヘッドが発生する.
どの程度のオーバーヘッドが気になるならば,virtual
禁止縛りで書いてみると面白いかもしれない.
#include "bits/stdc++.h" using namespace std; struct A{ const char type; // todo: うまいこと隠す int x; A(int _x = 1, char _type = 'A'):type(_type), x(_x){ cout << "construct A : " << x << endl; } ~A(); void func(); }; struct B: public A{ int y; B(int _y = 2, int _x = 1):A(_x, 'B'),y(_y){ cout << "construct B : " << x << ',' << y << endl; } void _delB(){ cout << "destruct B : " << x << ',' << y << endl; } void _func(){ cout << "func B : y=" << y << endl; } }; A::~A(){ if (type == 'B') ((B*)this)->_delB(); cout << "destruct A : " << x << endl; } void A::func(){ if (type == 'B') ((B*)this)->_func(); else cout << "func A : x=" << x << endl; } int main(){ unique_ptr<A> ptr; ptr.reset(new A(10)); ptr->func(); ptr.reset(new B(20, 30)); ptr->func(); A* p = new B(9, 99); p->func(); delete p; return 0; }
汚い