3
class Base {
public:
virtual void f(float) { cout << "Base::f(float)\n"; }
};
class Derived : public Base {
public:
virtual void f(int) { cout << "Derived::f(int)\n"; }
};
int main() {
Derived *d = new Derived();
Base *b = d;
b->f(3.14F);
d->f(3.14F);
}

-C++ は contravaraint 戻り値の型をサポートしていないため、f(int) は f(float) をオーバーライドしません。

-ポリモーフィズムは C++ でサポートされているため、d と b の両方が派生クラスの vtable を指す必要があります。

派生クラスの -vtable は、0: f(float)、1: f(int) などです。

質問に対する私の答えは Base::f(float) が2回呼び出されることですが、答えは次のとおりです。

Base::f(float) 派生::f(int)

これはなぜですか?別のポインターから派生クラスにアクセスすると、特定の規則が適用されますか? 私の知る限り、オブジェクトのスライスは、copy-ctor または copy-assignment を使用している場合にのみ発生し、ポインターを使用すると、それらはすべて同じ vtable を指す必要があります。

4

2 に答える 2

3

異なるクラスのメソッドが一緒にオーバーロードされることはありません。別のf()inを定義すると、オーバーロードの解決中にDerivedinBaseが考慮されなくなります。

基本クラスで定義されているものを非表示にせずに新しいオーバーロードを追加する場合は、派生クラスに基本定義を明示的に取り込む必要があります。

public:
    using Base::f;
于 2014-11-08T03:36:15.310 に答える
1

これに対する私の見解は、呼び出すときに float から int への暗黙的な変換があるということですd->f(3.14F)

クラスDerivedには実際に f() のエントリが 2 つあります

void f(int);
void f(float);

これは、-fdump-class-hierarchy フラグを指定した g++ で確認されています。出力は次のようになります。

Vtable for Base
Base::_ZTV4Base: 3u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI4Base)
8     (int (*)(...))Base::f

Vtable for Derived
Derived::_ZTV7Derived: 4u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI7Derived)
8     (int (*)(...))Base::f
12    (int (*)(...))Derived::f

Derived定義が一致しないため、クラスは基本クラスの f 関数に触れません。float と int の間に存在する暗黙的な変換があるため、コードはコンパイルされます。

以下を試してください

class Derived : public Base {
public:
virtual void f(std::string) { cout << "Derived::f(string)\n"; }
};

Derived:f(...) は Base::f(...) の定義を隠すため、このコードはコンパイルされません。これは、継承シャドウイングと呼ばれることがよくあります。

したがって、b->f(3.14F) の呼び出しは Base テーブル内で解決され、d->f(3.14F) の呼び出しは Base をシャドウする Derived テーブルで解決されます。

于 2014-11-08T04:27:14.840 に答える