仮想基本クラス
並列的継承の問題点
C++の継承が、いかに柔軟なものかは前回で理解していただけたと思います
ですが、継承によっては非常に難しい問題が発生します
継承が複雑になると、同じクラスを2度継承するという事態が発生します
基本クラス
|
---|
| | |
|
---|
派生クラス1 | 派生クラス2
|
---|
| | |
|
---|
派生クラス3
|
---|
図を見てわかるように、派生クラス3が基本クラスを2度継承しています
派生クラス1と派生クラス2が、基本クラスを継承しているためです
#include<iostream>
using namespace std;
class Base {
public:
char *name;
};
class Derived1: public Base {
public:
int age;
};
class Derived2 : public Base {
public:
char *sex;
};
class Derived3 : public Derived1 , public Derived2 {
public:
void print() {
cout << "名前 : " << name;
cout << "\t年齢 : " << age;
cout << "\t性別 : " << sex << '\n';
}
} obj ;
int main() {
obj.name = "前原しのぶ";
obj.age = 13;
obj.sex = "女";
obj.print();
return 0;
}
このプログラムだと、obj.name が非常に曖昧です
Derived1 と Derived2 クラスは、とくに曖昧ではありませんが
Derived3 は、Derived1の基本クラス Base と Derived2 の基本クラス Base を継承します
そのため、このプログラムはコンパイルできません
そこで、仮想基本クラスを用いることでこれを解決します
Base クラスを仮想基本クラスと宣言すると、派生クラスで2度継承する事態を避けられます
これを宣言するには virtual キーワードを用います
virtual [access-specifier] base-class-name
access-specifier には、アクセス指定子を
base-class-name には、仮想基本クラスの名前を指定します
アクセス指定子と virtual の順番は任意です
アクセス指定子が virtual の前にきても、問題はありません
#include<iostream>
using namespace std;
class Base {
public:
char *name;
};
class Derived1: virtual public Base {
public:
int age;
};
class Derived2 : virtual public Base {
public:
char *sex;
};
class Derived3 : public Derived1 , public Derived2 {
public:
void print() {
cout << "名前 : " << name;
cout << "\t年齢 : " << age;
cout << "\t性別 : " << sex << '\n';
}
} obj ;
int main() {
obj.name = "前原しのぶ";
obj.age = 13;
obj.sex = "女";
obj.print();
return 0;
}
Derived3 が継承した基本クラスは、仮想基本クラスとして Base を継承しています
そのため、Derived3 のオブジェクトは、Base のコピーを一つしか持ちません
こうすることで、このような並列的多重継承を保証することができます
virtual キーワードは、仮想基本クラスを持つ派生クラスからしか意味を持ちません
つまり、上のプログラムの Derived1 や Derived2 からは
継承した Base クラスは通常の基本クラスとして機能します
仮想基本クラスを持つ二つの基本クラスが
それぞれ別のアクセス指定子で継承している場合
派生クラスからアクセス制御は、もっともオープンなアクセス制御を取ります
#include<iostream>
using namespace std;
class Base {
public:
char *name;
};
class Derived1: virtual public Base {
public:
int age;
};
class Derived2 : virtual private Base {
public:
char *sex;
};
class Derived3 : public Derived1 , public Derived2 {
public:
void print() {
cout << "名前 : " << name;
cout << "\t年齢 : " << age;
cout << "\t性別 : " << sex << '\n';
}
} obj ;
int main() {
obj.name = "前原しのぶ";
obj.age = 13;
obj.sex = "女";
obj.print();
return 0;
}
Derived2 は private で Base クラスを継承していますが
Derived1 は public で Base クラスを継承しています
これらを継承した Derived3 は、Derived1 が public なので
問題なく Base クラスのメンバにアクセスすることができます
virtual
virtual [access-specifier] base-class-name
仮想基本クラスを宣言します
access-specifier - アクセスレベルを定義します。virtual の前でもかまいません
base-class-name - 仮想基本クラスとなるクラスを指定します