【C++】重荷、カバー、多態を考察するテーマ


コード:
#include <iostream>
#include <string>
using namespace std;
class A
{
protected:
    int m_data;
public:
    A(int data = 0)
    {
        m_data = data;
    }
    int GetData()
    {
        return doGetData();
    }
    virtual int doGetData()
    {
        return m_data;
    }
};
class B : public A
{
protected:
    int m_data;
public:
    B(int data = 1)
    {
        m_data = data;
    }
    int doGetData()
    {
        return m_data;
    }
};
class C : public B
{
protected:
    int m_data;
public:
    C(int data = 2)
    {
        m_data = data;
    }
};
void main()
{
    C c(10);

    cout << c.GetData() << endl;
    cout << c.A::GetData()<< endl;
    cout << c.B::GetData()<< endl;
    cout << c.C::GetData()<< endl;

    cout << c.doGetData()<< endl;
    cout << c.A::doGetData()<< endl;
    cout << c.B::doGetData()<< endl;
    cout << c.C::doGetData()<< endl;
}

質問:1.プログラム実行後の出力結果は何ですか?2.AクラスのdoGetData()関数のvirtual修飾を削除し、プログラム実行後の出力結果は何ですか?
分析:1.まず、データの初期化を見てみましょう.
C c(10);

CはBを継承し、BはAを継承するので、Cをインスタンス化するとき、AとBをインスタンス化します.
A,B,Cともにm_が定義されているdata、だからB、CはAの中のm_を継承しませんdata(同名で上書きされているため)、実際には3つの異なるネーミングスペースのm_dataがある.
サブクラスは親の構築方法を継承しないため、サブクラスオブジェクトを作成する際に、親から継承されたデータメンバーを初期化するために、親の構築方法を呼び出す必要があります.
そこで、A,B,Cの構成方法を順次呼び出す.
((A)c).m_data = 0;
((B)c).m_data = 1;
c.m_data = 10;

次に、関数を見てみましょう.
関数GetData()はclass Aの一般的なメソッドで、継承関係によってB、CクラスがGetData()メソッドにアクセスできます.B、CはGetData()を書き換えていないので、B、Cのオブジェクトを介してGetData()にアクセスし、アクセスするのはすべてAのGetData()メソッドです.
関数doGetData()は虚関数です.BはdoGetData()関数を書き換え、Cは書き換えないので、c.doGetData()を呼び出すと、BのdoGetData()が呼び出され、((B)c).m_dataが返されます.
ここでは、c.A::GetData()/c.GetData()/c.B::GetData()/c.C::GetData()
これらの文は、いずれもAのGetData()を呼び出すメソッドであり、呼び出し元はcであるためGetData()で呼び出されるdoGetData()はB::doGetData()であり、アクセス先はBのオブジェクトであり、そのm_data=1なのでdoGetData()は1を返し、最終GetData()は1を返します.
CクラスはdoGetData()関数を定義ので、c.doGetData()は(B)cに等しい.doGetData()は、Bのオブジェクトにアクセスし、そのm_data=1なのでc.doGetData()の結果は1です.
c.A::doGetData()、クラスドメインオペレータは呼び出しA::doGetData()を限定し、Aのオブジェクトにアクセスし、(A)cを返す.m_data = 0 .
c.B::doGetData()/c.::doGetData()は、最終的にはBオブジェクトにアクセスするdoGetData()メソッドであり、いずれも1を返します.
したがってdoGetData()はvirtualキーワードを持ち,プログラム出力は1 1 1 1 1 1 0 1である.
2.doGetData()が虚関数でない場合:
AクラスのGetData()内で呼び出されるdoGetData()メソッドは、Aのオブジェクトのメソッドのみ可能である、(A)c).m_data=0なので、前の4つのcoutの結果はすべて0です.
c.doGetData()は、Cのオブジェクト内にこの関数が実装されておらず、親クラス、すなわちBのオブジェクトを呼び出すdoGetData()メソッドであり、アクセスするm_dataは((B)c)オブジェクトで、1です.
c.A::doGetData()は、Aのメソッドを呼び出し、Aのオブジェクト(A)cにアクセスする.m_data、結果は0です.
c.B::doGetData()/c.::doGetData()は、Bのメソッドを呼び出し、Bのオブジェクト((B)cにアクセスする.m_data、結果は1です.
したがって、最終結果は0 0 0 0 1 0 1 1