1.仮想基本クラスの概念
以前に、抽象クラスは純粋な仮想関数を持つクラスであることを学びました。そして、仮想基本クラスとは何ですか?
まず第一に、私たちはvietualが2つの場所で修正されることができることを明確にすることができます。1つ目は、メンバーメソッドを仮想関数に変更することであり、2つ目は、継承メソッドを仮想継承に変更することです。仮想継承クラスは仮想基本クラスと呼ばれます
したがって、仮想基底クラスの定義を与えることができます:継承された仮想クラスは、仮想基底クラスvbptrおよびvbtableと呼ばれます。
たとえば、次のコードでは、クラスAは仮想基底クラスです。
class A
{
public:
private:
int ma;
};
class B:virtual public A
{
public:
private:
int mb;
};
これで、クラスのメモリ構造図は以下のようになります。
以前に描画した仮想関数のメモリ構造に従って、仮想ベースクラスがない場合は最初にメモリ構造を描画し、次にベースクラスがある場合はメモリマップを取得するように調整を行います。
vbptrは何を指していますか?以下に示すように:
2.仮想基底クラスと仮想関数の共存
上記のコードは単純な仮想基本クラスですが、仮想基本クラスに仮想関数を追加すると、メモリ構造はどうなりますか?
コードは次のとおりです。
class A
{
public:
virtual void func() { cout << "call A::func" << endl; }
private:
int ma;
};
class B :virtual public A
{
public:
virtual void func() { cout << "call B::func" << endl; }
private:
int mb;
};
int main()
{
A* p = new B();
p->func();
delete p;
return 0;
}
結果は次のとおりです。
なぜ上記のエラーが発生したのですか?さんは分析してみましょう
:まず第一に、我々は、対応するメモリ構造があるとして、次の描画
次のポインタpはそれが思考を指し?
基本クラスポインターは派生クラスオブジェクトを指し、次の図に示すように、常に派生クラス基本クラスのデータの一部の開始位置を指し
ます。エラーの理由は、メモリのリサイクル中にエラーが発生したためです。以下に示すよう
に、メモリの開発時にはvbptrである013855D0から開発されましたが、メモリの再利用時には仮想ベースクラスで起動され、メモリリークの問題がありました。
改善方法:破壊せずにメイン関数でスタック上のオブジェクトを開きます。オブジェクトはそれ自体でメモリを再利用します。次のコード:
int main()
{
B b;
A* p = &b;
cout << "main p:" << p << endl;
p->func();
return 0;
}
結果は次のとおりです。
3.仮想基底クラスによって解決されるダイヤモンド継承の問題
ひし形の継承については、次の図に示すように、間接継承とその他の問題のある継承メソッドがあります。
これには設計上の問題があり、派生クラスには間接基本クラスデータの複数のコピーがあります。たとえば、写真のmaです。特定のクラスに対応して、maが名前属性の場合、異議を唱えるためにDオブジェクトに2つの名前があります。
上記のひし形の継承の場合、コードの実装は次のとおりです。
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl;}
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
class B :public A
{
public:
B(int data) :A(data),mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
class D :public B,public C
{
public:
}
~D() { cout << "~D()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
結果は次のとおりです。
これから、Dのメモリレイアウトを次のように描画
できます。間接基本クラスの複数のコピーの問題を明確に見ることができます。したがって、仮想基本クラスを使用して問題を解決
できるため、仮想ポインターが示すオフセットを介してmaのデータにアクセスできます。
ここでは、クラスDでクラスAを初期化する必要があることに注意する必要があります。そうしないと、デフォルトのコンストラクターAが見つからないというエラーが発生します。コードは次のように実装されます。
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl;}
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
class B :virtual public A
{
public:
B(int data) :A(data),mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : virtual public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
class D :public B,public C
{
public:
D(int data) :A(data),B(data), C(data),md(data) { cout << "B()" << endl; }
D(int data) : B(data), C(data), md(data) { cout << "B()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
操作の結果は次のとおりです
。問題は仮想基本クラスによって解決されます。