2

質問がばかげていると思われた場合は、ご容赦ください。私は C++ の初心者で、ラムタイム ポリモーフィズムを研究していました。基本クラス オブジェクトが派生クラス関数 (非仮想) を呼び出すとどうなるか知りたいです。たとえば、コードを見てください

class base {
    public:
        virtual void vfunc() {cout << "This is base's vfunc().\n";}
};

class derived1 : public base {
    public:
        void vfunc() {cout << "This is derived1's vfunc().\n";}
};

int main()
{
    base *p, b;
    derived1 d1;

    p = &b;
    p->vfunc();

    p = &d1;
    p->vfunc();

    return 0;
}

目的の出力が得られた後、基本クラスから virtual キーワードを削除しました。プログラムを実行すると、出力は次のようになります。

This is base's vfunc().
This is base's vfunc().

どちらの場合も(ポインター操作に関して)何が起こったのかを誰かが説明してくれれば、とても感謝しています

ありがとう

4

2 に答える 2

2

仮想の場合、実行時に は、p->vfunc()指示p先が属するクラスのクラス定義 (メモリ内にある部分に関係なく) に移動し、その vtable (または同様の代替実装) を読み取って、どの関数を実行するかを判断するように指示します。電話。

vtables が次のようになっているとします。

baseの vtable:

----------------
vfunc | 0x2048
---------------- 

derived1の vtable:

----------------
vfunc | 0x2096
---------------- 

vfunc次に、vtables が指す場所に応じて、のベース バージョンまたは派生バージョンが呼び出されます。

非仮想の場合、これは発生せず、呼び出す関数はコンパイル時にコンパイラによって石に設定されます。コンパイル時に、コンパイラが把握できるのは、それpが「ベースへのポインター」タイプであり、すべてのp->vfunc()呼び出しを address を指すように変更することだけ0x2048です。

于 2012-09-22T16:42:34.820 に答える
1

ここで起こっていることは、ベース用と派生用の 2 つの vtable があるということです。ベースの vtable には のエントリが 1 つvfunc()あり、派生の vtable にも 1 つのエントリがあります。トリックは、派生が親の vtable を取得したときに、親の仮想メソッドのいずれかをオーバーライドしているかどうかを確認することです。そのため、そこにあったものを置き換えて、独自のバージョンを指すようにしました。

つまり、base 型のインスタンスを介して呼び出すとvfunc()、bases へのポインタを含む base vtable がチェックされますvfunc()。派生のインスタンスを介して呼び出すと、派生の へのポインターが見つかりますvfunc()

virtual キーワードを削除すると、コンパイラは仮想関数を検索しなくなるため、オブジェクトの実行時の型に基づいているのではなく、静的/コンパイル時の型に基づいています。

つまり、virtual キーワードがないと、コンパイラはベースであるポインターの型を調べるため、毎回ベース バージョンを呼び出します。

于 2012-09-22T16:42:05.663 に答える