shonen.hateblo.jp

やったこと,しらべたことを書く.

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;
}

汚い