2018年01月02日
《その217》 仮想派生 & p.265演習7-5
仮想派生
基底クラス A からの派生クラス B と、
同じく、基底クラス A からの派生クラス C があります。そして、
クラス D は、その2つのクラス B,C の両方を基底クラスとする多重継承クラスです。
// ------------------------------------
#include <iostream>
class A {
int a;
public:
A() : a(0) { }
// A のデフォルトコンストラクタは、a を 0 で
// 初期化します。
int get_a() const { return a; }
// a のゲッタ
void set_a(int x) { a = x; }
// a のセッタ
};
class B : virtual public A {
// 派生クラス B の中に
// 基底クラス部分オブジェクト A が含まれます。
// virtual を付けて定義された基底クラス A は、
// 仮想基底クラスとなります。
// この派生は、
// 仮想派生と呼ばれます。
public:
int get_a() const { return A::get_a(); }
void set_a(int x) { A::set_a(x); }
};
class C : virtual public A {
// 派生クラス C の中に
// 基底クラス部分オブジェクト A が含まれます。
// virtual 付きなので、仮想派生です。
public:
int get_a() const { return A::get_a(); }
void set_a(int x) { A::set_a(x); }
};
class D : public B, public C {
// 派生クラス D の中に
// 基底クラス部分オブジェクト B が含まれ、
// さらに、その B の中に、B の基底クラス
// 部分オブジェクトとしての A ★1.が含ま
// れます。
// 全く同様に、
// 派生クラス D の中に
// 基底クラス部分オブジェクト C が含まれ、
// さらに、その C の中に、C の基底クラス
// 部分オブジェクトとしての A ★2.が含ま
// れます。
// A は仮想基底クラスです。このような場合、
// ★1.と★2.の2つの A は、同じものであり、した
// がって、D型オブジェクトに含まれる A は
// 1つだけということになります。
//
// D の中に取り込まれた A は、本来なら2つ存在す
// るはずですが、仮想基底クラスであるため
// に、2つ目の A の実体は複製されません。
// A を仮想基底クラスにすることで、 D の中に2つ
// の Aが存在するという複雑な状況を回避す
// ることができます。
public:
int get_a() const { return B::get_a(); }
void set_a(int x) { C::set_a(x); }
};
int main() {
D d;
// D型のクラスオブジェクト d を生成。
d.set_a(99);
// D::set_a は、★2.の A、
// すなわち D に含まれる C の基底クラス
// 部分オブジェクト A のデータメンバ a
// に 99 をセットします。
std::cout << d.get_a() << '\n'; // 結果は 99
// D::get_a は、★1.の A、
// すなわち D に含まれる B の基底クラス
// 部分オブジェクト A のデータメンバ a
// の値を取得します。
// A は1つしかありませんから、その値は
// 99 です。
}
// ------------------------------------
以下は、仮想派生が行われない場合の例です。
// ------------------------------------
#include <iostream>
class A {
int a;
public:
A() : a(0) { }
// A のデフォルトコンストラクタは、a を 0 で
// 初期化します。
int get_a() const { return a; }
// a のゲッタ
void set_a(int x) { a = x; }
// a のセッタ
};
class B : public A {
// 派生クラス B の中に
// 基底クラス部分オブジェクト A が含まれます。
public:
int get_a() const { return A::get_a(); }
void set_a(int x) { A::set_a(x); }
};
class C : public A {
// 派生クラス C の中に
// 基底クラス部分オブジェクト A が含まれます。
public:
int get_a() const { return A::get_a(); }
void set_a(int x) { A::set_a(x); }
};
class D : public B, public C {
// 派生クラス D の中に
// 基底クラス部分オブジェクト B が含まれ、
// さらに、その B の中に、B の基底クラス
// 部分オブジェクトとしての A ★1.が含ま
// れます。
// 全く同様に、
// 派生クラス D の中に
// 基底クラス部分オブジェクト C が含まれ、
// さらに、その C の中に、C の基底クラス
// 部分オブジェクトとしての A ★2.が含ま
// れます。
// A は仮想基底クラスではないので、
// ★1.と★2.の2つの A は、それぞれ別のものです。
public:
int get_a() const { return B::get_a(); }
void set_a(int x) { C::set_a(x); }
};
int main() {
D d;
// D型のクラスオブジェクト d を生成。
d.set_a(99);
// D::set_a は、★2.の A、
// すなわち D に含まれる C の基底クラス
// 部分オブジェクト A のデータメンバ a
// に 99 をセットします。
std::cout << d.get_a() << '\n'; // 結果は 0
// D::get_a は、★1.の A、
// すなわち D に含まれる B の基底クラス
// 部分オブジェクト A のデータメンバ a
// の値 0 を取得します。
// ★1.の A と★2.の A は別のものなので
// 99 ではなく、0 になります。
}
// ------------------------------------
新版明解C++中級編 p.265 演習7-5
次のプログラムは、仮想派生を行ったクラス型オブジェクトの初期化の際、各部分オブジェクトの初期化がどのような順序で実行されるのかを確認するためのものである。
プログラム中の各クラスにデストラクタを追加して、オブジェクト解体の順序の確認もできるようにせよ。
// ------------------------------------
#include <iostream>
using namespace std;
class V1 {
public:
V1() { cout << "V1を初期化\n"; }
};
class V2 {
public:
V2() { cout << "V2を初期化\n"; }
};
class X
: virtual public V1, virtual public V2 {
public:
X() { cout << "X を初期化\n"; }
};
class Y
: virtual public V2, virtual public V1 {
public:
Y() { cout << "Y を初期化\n"; }
};
class Z : public X, public Y {
public:
Z() { cout << "Z を初期化\n"; }
};
int main() {
Z dummy;
}
// ------------------------------------
以下が、解答のプログラムです。
// ------------------------------------
#include <iostream>
using namespace std;
class V1 {
public:
V1() { cout << "V1を初期化\n"; }
~V1() { cout << "V1を解体\n"; }
};
class V2 {
public:
V2() { cout << "V2を初期化\n"; }
~V2() { cout << "V2を解体\n"; }
};
class X
: virtual public V1, virtual public V2 {
public:
X() { cout << "X を初期化\n"; }
~X() { cout << "X を解体\n"; }
};
class Y
: virtual public V2, virtual public V1 {
public:
Y() { cout << "Y を初期化\n"; }
~Y() { cout << "Y を解体\n"; }
};
class Z : public X, public Y {
public:
Z() { cout << "Z を初期化\n"; }
~Z() { cout << "Z を解体\n"; }
};
int main() {
Z dummy;
}
// ------------------------------------
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/7152625
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック