6

次のコードを検討してください。

class Abase{};  
class A1:public Abase{};  
class A2:public A1{};  
//etc  

class Bbase{  
public:  
    virtual void f(Abase* a);  
    virtual void f(A1* a);  
    virtual void f(A2* a);  
};

class B1:public Bbase{  
public:
    void f(A1* a);  
};

class B2:public Bbase{  
public:
    void f(A2* a);
};  

int main(){  
    A1* a1=new A1();  
    A2* a2=new A2();  
    Bbase* b1=new B1();  
    Bbase* b2=new B2();  
    b1->f(a1); // calls B1::f(A1*), ok  
    b2->f(a2); // calls B2::f(A2*), ok  
    b2->f(a1); // calls Bbase::f(A1*), ok  
    b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)! 
}  

thisの引数をアップキャストするのではなく、オブジェクトのポインターを基本クラスにアップキャストすることによって、C++ が最終行の関数呼び出しを解決することを選択する理由を知りたいf()です。私が望む動作を得る方法はありますか?

4

5 に答える 5

10

呼び出すバージョンの選択は、パラメーターのコンパイル時のタイプをf調べることによって行われます。この名前解決では、実行時型は考慮されません。はタイプであるため、のすべてのメンバーが考慮されます。を取るものが最適な一致であるため、それが呼び出されます。b1Bbase*BbaseA2*

于 2010-06-23T15:59:41.687 に答える
2

「...オブジェクトの this ポインターを基本クラスにアップキャストすることにより、最後の行で関数呼び出しを解決することを選択します...」 . あなたは何について話していますか?すべての呼び出しで、オブジェクト ポインターの型はであり、呼び出しが解決される関数は、いずれかまたはその子孫Bbase *に属します。Bbaseコンパイラは、呼び出しを解決するためにアップキャストを行いません。実際、最初の 2 つの呼び出しでは、適切なオーバーライダーを呼び出すためにダウンキャストが必要です。これは、オーバーライダーが階層のさらに下にあるクラスに属しているためです。最後の 2 つの呼び出しについては、型Bbaseのポインターを介してクラスにディスパッチされますBbase *。タイプは正確に一致し、いかなる種類のキャストも行われません。

オーバーロードの解決については... オーバーロードの解決はコンパイル時のプロセスであり、引数の静的な型と可能な変換のランクに基づいています。A2 *タイプの引数を指定しました。f(A2 *)候補者はあなたの主張と正確に一致しました。f(A1 *)候補には、 からA2 *への追加の変換が必要A1 *です。正確に一致する候補はより良い候補と見なされるため、オーバーロードの解決に勝ちます。単純。

于 2010-06-23T16:30:51.163 に答える
1
b1->f(static_cast<A1*>(a2));

これにより、コンパイラは A1 型のパラメーターでオーバーロード メソッドを使用するようになります。

于 2010-06-23T15:43:15.693 に答える
0

Abase と A2 の Bbase のオーバーロードは、B1 に隠されています。おそらく、この問題を次のように回避できます。

class Bbase{  
public:
    inline void f(Abase* a) { f_(a); }
    inline void f(A1* a) { f_(a); } 
    inline void f(A2* a) { f_(a); } 
protected:
    virtual void f_(Abase* a);  
    virtual void f_(A1* a);  
    virtual void f_(A2* a);  
};

class B1:public Bbase{  
protected:
    void f_(A1* a);  
};

class B2:public Bbase{  
protected:
    void f_(A2* a);
}; 

または Bbase のテンプレートを使用:

class Bbase{  
public:
    template<class myA>
    inline void f(myA* a) { f_(a); }
protected:
    virtual void f_(Abase* a);  
    virtual void f_(A1* a);  
    virtual void f_(A2* a);  
};
于 2010-06-23T16:42:07.297 に答える
0

名前隠しといいます。1 つの派生クラスで宣言するすべての f は、その基底クラスのいずれかで可能なすべての f をシャドウします。

基本クラスへのキャストを使用して、目的の動作を取得します。

仮想関数をオーバーライドする場合、同じ名前のオーバーロードされた関数をオーバーライドしません。それらは異なる機能です (そして vtable に異なるエントリがあります)。

于 2010-06-23T15:45:43.850 に答える