1
class base
{
public:
    virtual void showbase() {
        // ----------
    }
};

class base1 {
public:
    virtual void showbase1() {
        // -------
    }
};

class derived : public base, public base1
{
    void showbase() {
        // ----  
    }

    void showbase1() {
        // -------
    }
};

int main()
{
    base* p = new derived();
    p->showbase1();

    base1* p1 = new derived();
    p1->showbase();
}

仮想関数についての私の理解によると、コンパイラはそれを実行時 (vtable メカニズム) で処理し、なぜコンパイル時エラーが発生するのかということです。

4

5 に答える 5

2

コンパイラをシミュレートするには、コンパイラが見るものを考慮してください。

class base
{
public:
    virtual void showbase() {
        // ----------
    }
};

base* p = /*blah blah*/;
p->showbase1();

はい、baseポリモーフィック クラスです。そしてp、確かに へのポインタbaseです。しかし、pは a のみを指しbase、重要なことに(どこに住んでいるか) を指していないため、コンパイラーは上記のコードを次のように解釈します。明らかに、私は言い換えています:base1showbase1

Here is a class named `base` with a single virtual method called `showbase`.  
Here is a pointer to a `base` object.  Call the method named `showbase1`

そして、コンパイラは不平を言います:

ええと、失礼ですbaseが、 というメソッドがありません showbase1


あなたは尋ねました:

[私の]仮想関数についての理解は、コンパイラが実行時に仮想関数を処理するということです。コンパイル時エラーが発生するのはなぜですか?

あなたが書いたコードはナンセンスだからです。これが基本的にポリモーフィズムの仕組みです。

  1. 仮想メソッドを使用して基本クラスを定義します。
  2. これらの仮想メソッドをオーバーライドする派生クラスを定義します。
  3. コンパイラは、基本クラスのメソッドの名前を派生クラスの実装にマップする vtable を作成します。
  4. 基本クラスへのポインター (または参照) を介して基本クラスのメソッドを呼び出すと、派生クラスの実装が呼び出されます。

しかし、あなたがやろうとしていることは次のとおりです。

  1. 仮想メソッドを使用して基本クラスを定義します。
  2. これらの仮想メソッドをオーバーライドする派生クラスを定義します。
  3. まったく別のクラスで関数を呼び出します。
于 2013-10-21T20:37:41.517 に答える
1

仮想関数についての私の理解によると、コンパイラはそれを実行時 (vtable メカニズム) で処理し、なぜコンパイル時エラーが発生するのかということです。

「それを扱う」はかなり曖昧で、vtables は魔法ではありません。C++ では、仮想ディスパッチにより、呼び出される実際の関数を、静的に宣言された仮想関数をオーバーライドする関数にすることができます。つまり、オーバーライドされる関数はコンパイル時に認識されている必要があります。

vtable には、実行時に関数を検索するために必要な情報が含まれていません。代わりに、基本的にはオーバーライド関数へのポインターのリストにすぎません。ベースはその仮想関数の完全なリストを提供するため、特定のベース タイプが与えられると、コンパイラは特定の関数オーバーライドのためにそのベースの vtable 内のどこに移動するかをコンパイル時に認識します。コンパイラは、vtable 内のその場所に直接移動し、ポインターを取得し、オーバーライド関数を呼び出すコードを生成できます。

次に、実行時に派生型の実際のオブジェクトが作成されると、派生オブジェクトのコンストラクターがベースの vtable を埋めて、vtable をチェックするすべてのものが派生型の関数へのポインターを取得するようにします。

したがって、コードの問題は、呼び出している関数 がshowbase()、コンパイラがアクセスしていることを認識している型の仮想関数のリストにないことですbase1。コンパイラは、の vtable にそのようなエントリがないため、base1'sという名前の関数オーバーライドのポインタを取得する vtable の場所を認識できません。showbase()base1

于 2013-10-21T20:52:04.177 に答える