私には3つの異なる基本クラスがあります:
class BaseA
{
public:
virtual int foo() = 0;
};
class BaseB
{
public:
virtual int foo() { return 42; }
};
class BaseC
{
public:
int foo() { return 42; }
};
次に、次のようなベースから派生します(A、B、またはCの代わりにXを使用します)。
class Child : public BaseX
{
public:
int foo() { return 42; }
};
3つの異なる基本クラスで関数はどのようにオーバーライドされますか?私の次の3つの仮定は正しいですか?他に注意点はありますか?
派生クラスでは、キーワードvirtualが派生クラスのメソッドで使用されていなくても、基本クラスで仮想として定義されている場合、メソッドは仮想です。
BaseA
を使用すると、意図したとおりにコンパイルおよび実行され、foo()
は仮想であり、クラスChild
で実行されます。BaseB
と同じように、意図したとおりにコンパイルおよび実行されます。foo()
はvirtual()であり、クラスChild
で実行されます。BaseC
を使用すると、コンパイルして実行されますが、BaseC
およびBaseC
のコンテキストから呼び出すと、Child
バージョンが実行されます。 Child
のコンテキストで呼び出す場合のバージョン。覚えておくべき重要なルールは、関数が仮想として宣言されると、派生クラスでシグネチャが一致する関数は常に仮想になるということです。したがって、Child ofAとChildof Bはオーバーライドされ、同じように動作します(BaseAを直接インスタンス化できないことを除いて)。
ただし、Cを使用すると、関数はオーバーライドされませんが、オーバーロードされます。そのような状況では、静的型のみが重要です。オブジェクトが実際に何であるか(動的型)ではなく、それがポインターであるもの(静的型)で呼び出されます。
BaseAでは、子クラスはコンパイルされず、純粋仮想関数は定義されません
これは、BaseAのオブジェクトを作成しようとした場合にのみ当てはまります。 Childのオブジェクトを作成してから、BaseA *またはChild *のいずれかを使用してfoo()を呼び出すことができます。
BaseBでは、BaseB *またはChild *でfooを呼び出すときに、子の関数が呼び出されます。
オブジェクトはBaseBまたはChildのいずれかである可能性があるため、オブジェクトのタイプによって異なります。オブジェクトがBaseBの場合、BaseB :: fooが呼び出されます。
BaseCでは、Child *でfooを呼び出すときに子の関数が呼び出されますが、BaseB *では呼び出されません(親クラスの関数が呼び出されます)。
はい、しかしあなたはこれをしたくありません。
多態性の観点からは、Aを優先します。これにより、各子が仮想関数の独自の実装を持っていることがわかります。
主に有効なデフォルトの実装がある場合はBを選択しますが、必要に応じてすべての子クラスに独自の実装があることを確認する必要があります。 Cはポリモーフィズムではないため、慎重に使用してください。
それは主にあなたがそれをどのように呼んだかに依存します。
あなたがした場合:
class Child : public BaseA
{
public:
int foo() { return 42; }
};
そしてしました
BaseA baseA = new Child();
baseA->foo();
子のfoo関数を呼び出します。
ただし、これを行った場合:
BaseA baseA = new BaseA();
コンパイル時エラーが発生します。
クラスChild will Aから派生した場合はコンパイルしますが、そのタイプのオブジェクトをインスタンス化することはできません。
これは、Baseからいくつかの関数をオーバーライドしてから、再度派生させる場合に役立つ可能性があります。