web-dev-qa-db-ja.com

仮想関数が非表示になるのはなぜですか?

私は次のクラスを持っています:

class A {
public:
    virtual void f() {}
};


class B : public A{
public:
    void f(int x) {}
};

私が言うなら

B *b = new B();
b->f();

コンパイラはエラーC2660を言います: 'B :: f':関数は0個の引数を取りません。 Bの関数は仮想関数なので、オーバーロードするべきではありませんか?仮想関数はこのように隠されますか?

[〜#〜] edit [〜#〜]:私は確かにAからBを継承するつもりでしたが、これは同じ動作を示しています。

22
Oszkar

BAから派生することを意図していると仮定します。

f(int)f()は異なるシグネチャであるため、異なる関数です。

override互換性のあるシグニチャを持つ関数を持つ仮想関数、つまり同一のシグニチャ、または戻り値の型が「より具体的」なもの(これは共分散)のいずれかを意味します。

それ以外の場合、派生クラスが基本クラス関数と同じ名前の関数を宣言する他の場合と同様に、派生クラス関数は仮想関数を非表示にします。クラスBに_using A::f;_を入れて、名前を再表示できます

または、_(static_cast<A*>(b))->f();_またはb->A::f();と呼ぶこともできます。違いは、Bが実際にf()をオーバーライドする場合、前者はオーバーライドを呼び出すのに対し、後者はA内の関数を呼び出すことです。

37
Steve Jessop

クラスBはAから派生していないため、関数F()は存在しません。おそらく次のことを意味します。

class A {
public:
    virtual void f() {}
};


class B : public A {
public:
    void f(int x) {}
};

編集:実際の関数の非表示を見逃しました。より詳細な説明については、SteveJessopの回答を参照してください。

7
Rod

それぞれ、いいえ、はい。過負荷の動作が必要な場合は、次のように言う必要があります

using A::f;

bで。

4
Stuart Golodetz

BはAから派生したものではなく、正しい宣言は次のとおりです。

class B : public A
2
Moo-Juice

コンパイラがシンボルを解決する方法が複数ある場合、コードで特に指示されていない限り、どちらが優先されるかを選択する必要があります。期待しているのは、オーバーロードがオーバーライドよりも優先されることです。 (オーバー、オーバー、オーバー、aaaaack!申し訳ありませんが、 'オーバー'圧倒されました)。

この例では、サブクラスがオーバーロードされたバージョンを提供する仮想メソッドを継承するBがあります。オーバーロードは、同じメソッド名で異なるシグニチャを使用する同じクラスのメソッドに対するものです。 BはAのサブクラスであるため、f()をオーバーライドします。これは、同時にオーバーロードになることもできないことを意味します。これが隠されている理由です。

クラスAの場合、メソッドの宣言

virtual void f() {}  

仮想としては、bの宣言と一致しない特定のルールセットを使用してメソッドが解決されることを意味します。

B *b = new B();

「B」のインスタンスとして「b」を作成することにより、コンパイラは「A」の同じ名前のメソッドの仮想的な性質を使用する必要がなくなります。

このように「b」を宣言した場合

B *b = new A();  

次に、b-> f();を呼び出します。実際、仮想解像度を利用してAのメソッドを参照します。

2
Kelly S. French

Biern StroustrupのFAQに回答のあるかなり類似した質問が存在するようです: http://www.stroustrup.com/bs_faq2.html#overload派生

彼が言ったように:

「C++では、スコープ間でのオーバーロードはありません」

でももしあなたが欲しければ

「これは、using宣言を使用して簡単に実行できます」

2
bruziuz