2012-02-02 7 views
8
class base { 
public: 
    void virtual fn(int i) { 
     cout << "base" << endl; 
    } 
}; 

class der : public base{ 
    public: 
    void fn(char i) { 
     cout << "der" << endl; 
    } 
}; 

int main() { 

    base* p = new der; 
    char i = 5; 
    p->fn(i); 
    cout << sizeof(base); 
    return 0; 
} 

ここ署名はderクラスで定義された関数fn()のシグネチャとは異なります。 したがって、derクラスで定義された関数は、baseクラス関数fn()を隠します。したがって、クラスderのfnの呼び出しはp->fn(i)コールでは呼び出せません。それは結構です。派生クラスが仮想関数をオーバーライドしない場合にvptrが必要なのはなぜですか?関数名は同じであるが<code>base</code>クラスで定義された関数fnの

私のポイントは、ある理由VTABLEポインタのない使用がない場合sizeofクラスbaseまたはder4のですか? VTABLEポインタの要件は何ですか?

+0

C++でオーバーロードについて聞いたことがありますか? –

+0

@KamilKlimek:オーバーロードは、異なるシグネチャで複数の関数を宣言する行為です。おそらく、オーバーライド(派生クラスのメソッドを再実装すること)が意味します。あなたの種類の情報のために –

+0

、これらは過負荷機能ではありません。 – user966379

答えて

6

これは非常に実装に依存することに注意してください。&はコンパイラによって異なる場合があります。

vtableの存在の要件は、基本クラスが継承と拡張を対象としており、それから派生するクラスがメソッドをオーバーライドする可能性があることです。

基本クラスと派生クラスの2つのクラスは異なる翻訳単位に存在する可能性があり、コンパイラは基本クラスをコンパイルしてもメソッドがオーバーライドされるかどうかは分かりません。したがって、キーワードvirtualが見つかった場合は、vtableが生成されます。

+1

しかし、この例では、すべてのコード*は同じモジュール内にあり、コンパイラは知ることができます。 –

0

継承はis-aの関係です。 derは-baseです。 baseは、サイズ4を有し、derは、少なくともサイズ4を有する。 vftableptrbaseのメンバーで、derのメンバーになります。

baseには仮想メソッドがあるため、使用するかどうかにかかわらず、仮想テーブルへのポインタがあります。

+0

あなたはポイントを見落としていますが、Q OPの質問は*なぜベースクラスのサイズが4であるのか*ではない*派生クラスのサイズが4の理由* –

+0

派生クラスのどれもがvirtual fucntn –

+0

@Als私はかなりわかりやすいので、混乱しました。それは仮想メソッドを持っています、なぜそれがvtableへのポインタを持ってはいけないのですか?私はこれを反映するために私の答えを編集しました。 –

1

vtableは、通常仮想関数に使用されるだけでなく、dynamic_castを実行したとき、またはクラスのtype_infoにアクセスするときにクラスの種類を識別するためにも使用されます。コンパイラは何の仮想関数がこれまでに上書きされていないことを検出し、他の機能のいずれも使用されていない場合

は、それだけで最適化としてのvtableポインタを削除することができます。

明らかに、コンパイラの作家はこれを行うのに苦労する価値はないと思います。おそらく、頻繁に使用されることはなく、基本クラスからvirtualを削除することで、自分で行うことができるからです。

+0

仮想B(int)、B:パブリックA - オーバーライドなし、C:パブリックB override b(int)、D:パブリックC - オーバーライドされません。そして、この場合、コンパイラ以外のどのような振る舞いをするのですか?どこでvtableでなければならないべきか?もし私がB *インスタンス=新しいD; ? –

+0

また、プラグインの工場として使用されている場合はどうなりますか?仮想メソッドをオーバーライドする継承クラスがあるかどうかを知るための物理的な方法はありません。 –

+0

オーバーライドがどこにあっても、vtableはどこにでもなければなりません。各クラスのバージョンは1つしかないからです。コンパイラが必要でないこと、最適化できることをコンパイラが確実に伝えることができます –

1

以下が含まれます、同じまたは別のプロジェクト内の別のソースファイルがある可能性があるため、コンパイラは、「基本」クラスのうち、vtableメンバ変数を最適化することはできません。

struct ived : base { 
    ived() : p(new char[BIG_DATA_SIZE]) {} 
    virtual ~ived(); 
    virtual void fn(int); 
private: 
    char* p; 
}; 

デストラクタとfn可能性どこかに実装される:

ived::~ived() { delete[] p; } 

void ived::fn(int) { 
    cout << "ived" << endl; 
} 

そして、どこか別の場所でこのようなコードがあるかもしれません:

base* object = new ived; 
ived->fn(0); 
delete object; 
cout << sizeof(base) << endl; 

したがって、仮想関数ived::fnは呼び出されず、仮想デストラクタが呼び出されないので、BIG_DATA_SIZEは削除されません。そうでない場合はsizeof(base)が異なります。そのため、コンパイラは常に、仮想メンバ関数または仮想基本クラスを持つ任意のクラスに対してvtableを生成します。

派生クラス内のデストラクタの呼び出しに関しては、それは必須と見なす必要があります。仮想関数を持つクラスがあれば、そのクラスは仮想デストラクタも宣言します。