虚继承 -- 解决菱形继承问题以及无法跨继承访问
目录
什么是菱形继承?
菱形继承造成的问题:
如何解决这种问题:
代码分析:
注意:
什么是菱形继承?
上图就是一个菱形继承的例子。
菱形继承: 有两个类(Father,Mother),都继承于一个类(Human),然后还有一个类,又同时继承于这两个类(Son),这样就构成了菱形继承。
菱形继承造成的问题:
1. 会导致继承于Human类中的属性形成二义性,因为Father类从Human中继承了name和age,Mother类也从Human中继承了name和age,子类Son同时继承了父类和母类的name和age。所以在子类中直接访问name和age,编译器就不知道访问的是哪个类继承来的name和age(二义性)
访问就会报错 -> 属性不明确。
如何解决这种问题:
菱形继承中,必然会存在不明确的属性,和多继承中有所不同(因为多继承中有时可以避免)。为了能够方便写代码(直接访问name和age),我们可以使用虚继承来解决。
class Human {
public:
Human();
Human(const string& name, int age);
protected:
string name;
int age;
};
class Father : virtual public Human {
public:
Father();
Father(const string& name, int age);
};
class Mother : virtual public Human {
public:
Mother();
Mother(const string& name, int age);
};
class Son : public Father, public Mother {
public:
Son(const string& name, int age);
};
int main(void) {
system("pause");
return 0;
}
Human::Human()
{
}
Human::Human(const string& name, int age)
{
this->name = name;
this->age = age;
}
Father::Father()
{
}
Father::Father(const string& name, int age):Human(name,age)
{
}
Mother::Mother()
{
}
Mother::Mother(const string& name, int age):Human(name,age)
{
}
Son::Son(const string& name, int age)
{
this->name = name;
this->age = age;
}
代码分析:
1. 我们在Father和Mother类继承前加上了virtual关键字,这样在Son的构造函数中就可以直接访问name和age了,因为虚继承之后,编译器认为name和age既不来自于Father,也不来自于Mother,而是直接访问的Human类中的,这样就不会有二义性了。
2. 那么共同的祖先就被称为虚基类(此处的Human)
3. 实现虚继承后,我们在初始化继承来的name和age属性的时候,也可以直接调用Human的构造函数。
这样也是可以的。
注意:
1. 虽然多继承中也有可能存在二义性,但是是不能使用虚继承来解决的。(仔细对比就知道,菱形继承存在三层继承关系,多继承一般就两层)
2. 只有在实现虚继承的时候,子类才能调用父类的父类的(也就是Son调用Human)类中的属性和方法,其它情况,子类最多只能访问父类的。 (父类的父类等等,是不能访问的)
Huamn类中的test()方法,在没有实现虚继承的情况下,也会报错->不明确,因为这个方法,Father类和Mother类中也继承了。
不使用虚继承,直接使用Human类的构造方法,也是会报错的。
3. 所以,多层继承中,子类最多只能访问到上一层。(在没有实现虚继承的情况下)