web-dev-qa-db-ja.com

オーバーライドと仮想

関数の前で予約語仮想を使用する目的は何ですか?子クラスで親関数をオーバーライドする場合は、void draw(){}などの同じ関数を宣言するだけです。

_class Parent { 
public:
    void say() {
        std::cout << "1";
    }
};

class Child : public Parent {
public:
    void say()
    {
        std::cout << "2";
    }
};

int main()
{
    Child* a = new Child();
    a->say();
    return 0;
}
_

出力は2です。

繰り返しになりますが、なぜsay()のヘッダーに予約語virtualが必要なのでしょうか?

本当にありがとう。

39
anonymous

これは、ポリモーフィズムがどのように機能するかという古典的な質問です。主なアイデアは、各オブジェクトの特定のタイプを抽象化することです。つまり、子であることを知らずにChildインスタンスを呼び出すことができるようにしたいのです!

次に例を示します。クラス「Child」とクラス「Child2」および「Child3」があると仮定して、それらの基本クラス(親)を介してそれらを参照できるようにしたいとします。

Parent* parents[3];
parents[0] = new Child();
parents[1] = new Child2();
parents[2] = new Child3();

for (int i=0; i<3; ++i)
    parents[i]->say();

ご想像のとおり、これは非常に強力です。親を必要な回数だけ拡張することができ、親ポインターを取る関数は引き続き機能します。他の人が言及したようにこれを機能させるには、メソッドを仮想として宣言する必要があります。

18
krolth

関数が仮想の場合、これを実行しても、出力「2」を取得できます。

Parent* a = new Child();
a->say();

これは、virtual関数がactualタイプを使用するのに対して、非仮想関数はを使用するためです。宣言済みタイプ。 polymorphism を読んで、なぜこれをしたいのかについてのより良い議論をしてください。

37
Donnie

で試してください:

Parent *a = new Child();
Parent *b = new Parent();

a->say();
b->say();

virtualなし、両方とも印刷 '1'あり。仮想を追加すると、Parentへのポインターを介して参照されている場合でも、子は子のように動作します。

26
Jerry Coffin

virtualキーワードを使用しない場合、オーバーライドはしませんが、派生クラスで基本クラスメソッドを非表示にする無関係なメソッドを定義します。つまり、virtualBase::sayおよびDerived::sayは、名前の一致以外に無関係です。

Virtualキーワード(ベースでは必須、派生クラスではオプション)を使用すると、このベースから派生するクラスがメソッドをオーバーライドできることをコンパイラーに伝えています。その場合、Base::sayおよびDerived::sayは、同じメソッドのオーバーライドと見なされます。

基本クラスへの参照またはポインターを使用して仮想メソッドを呼び出すと、コンパイラーは適切なコードを追加して、final overriderが呼び出されるようにします(メソッドを定義する最も派生したクラスのオーバーライド使用中の具象インスタンスの階層内)。参照/ポインタではなくローカル変数を使用する場合、コンパイラは呼び出しを解決でき、仮想ディスパッチメカニズムを使用する必要がないことに注意してください。

考えられることはたくさんあるので、自分でテストしました。

#include <iostream>
using namespace std;
class A
{
public:
    virtual void v() { cout << "A virtual" << endl; }
    void f() { cout << "A plain" << endl; }
};

class B : public A
{
public:
    virtual void v() { cout << "B virtual" << endl; }
    void f() { cout << "B plain" << endl; }
};

class C : public B
{
public:
    virtual void v() { cout << "C virtual" << endl; }
    void f() { cout << "C plain" << endl; }
};

int main()
{
    A * a = new C;
    a->f();
    a->v();

    ((B*)a)->f();
    ((B*)a)->v();
}

出力:

A plain
C virtual
B plain
C virtual

良い、シンプルで短い答えは次のように見えると思います(より多くを理解できる人は記憶することが少なく、したがって短く簡単な説明が必要だと思うので):

仮想メソッドは、ポインターが指すインスタンスのDATAをチェックしますが、従来のメソッドは、指定されたタイプに対応するメソッドを呼び出しません。

この機能のポイントは次のとおりです。Aの配列があるとします。配列には、B、C、または派生型を含めることができます。これらすべてのインスタンスの同じメソッドを順番に呼び出したい場合は、オーバーロードした各メソッドを呼び出します。

私はこれを理解するのが非常に難しいと思いますが、明らかにC++コースはこれがどのように達成されるかを説明する必要がありますコールを処理します、あなたは暗いです。

VFtablesについてのことは、どの種類のコードを追加するのか説明されたことがないことです。C++がCよりも多くの経験を必要とするのは明らかです。実際、それは強力ですが、すべてと同じように、使い方を知っていれば強力です。さもなければ、「足を吹き飛ばす」だけです。

12
jokoon

キーワードvirtualを使用すると、インスタンス内の正しいメソッドを見つけるための仮想関数テーブルが作成されます。次に、派生インスタンスが基本クラスポインターによってポイントされている場合でも、メソッドの正しい実装を見つけます。

2
Amardeep AC9MF