菱形继承关系
1、菱形继承是什么
答:一个派生类有多个基类,多个基类又由同一个类派生
2、菱形继承有什么问题
答:会让高层的基类在底层的派生类中拥有多份成员,造成不合理
3、菱形继承如何解决
答:利用虚继承解决,在虚继承中会在本来和保存虚基类的地方保存vbptr,
如果一个类中有重复的虚基类,就会让多个vbptr指向同一个虚基类作用域。
4.错误的菱形继承关系示例代码:
#include <iostream>
using namespace std;
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
class C:public A
{
public:
int _c;
};
class D:public B, public C
{
public:
int _d;
};
int main()
{
D d;
//d._a; error 出现D::_a不明确的错误,编译器不知道调用哪个类中的_a
d.B::_a; //ok
d.C::_a; //ok
return 0;
}
5.改进的菱形继承关系,通过虚继承实现
#include <iostream>
using namespace std;
class A //虚基类A
{
public:
int _a;
};
class B:virtual public A //虚继承,一种继承方式
{
public:
int _b;
};
class C:virtual public A
{
public:
int _c;
};
class D:public B, public C
{
public:
int _d;
};
int main()
{
D d;
d._a; //ok
return 0;
}
6.普通继承与虚继承的关系图
7. 纯虚函数,抽象类
纯虚函数:(1) 将成员函数声明为virtual; (2) 后面加上 = 0;(3)纯虚函数没有函数体
纯虚函数的作用:在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它说明为纯虚函数,它的实现留给其派生类去做,这就是纯虚函数的作用。
抽象类:含有纯虚函数的类叫做抽象类(又叫纯虚类,接口类)。抽象类是为了抽象和设计实现的。抽象类不能实例化对象,强制要求派生类实现某个接口。
之所以要存在抽象类,最主要是因为它具有不确定因素。我们把那些类中的确存在,但是在父类中无法确定具体实现的成员函数称为纯虚函数。纯虚函数是一种特殊的虚函数,它只有声明,没有具体的定义。抽象类中至少存在一个纯虚函数;存在纯虚函数的类一定是抽象类。存在纯虚函数是成为抽象类的充要条件。
class Base //抽象类(又叫接口类或纯虚类)
{
public:
virtual void show() = 0; //纯虚函数
};
/*
class Drive1 :public Base
{
public:
virtual void show() = 0;
int _a;
};
*/
class Drive2 :public Base
{
public:
virtual void show()
{
cout<<"Drive2::virtual void show()"<<endl;
}
int _b;
};
int main()
{
Drive1 d1; //error不允许使用抽象类Drive1类的对象。因为函数void show()是纯虚函数,所调用的派生类需要实现父类(抽象类Base)的纯虚函数。
Drive2 d2; //ok
return 0;
}
虚基类定义:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。
c++提供虚基类(class Drive : virtual public Base )的方法,使得在继承间接共同基类时只保留一份成员。
注意:(1)虚基类并不是在基类中声明的,而是在声明派生类时,指定继承方式时声明的。
(2)为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。
class A //虚基类
{
public:
int _a;
virtual void showA(){}
};
class B : virtual public A
{
public:
int _b;
virtual void showB()
{}
};
class C :virtual public A
{
public:
int _c;
virtual void showC()
{}
};
class D : public B, public C
{
public:
int _d;
virtual void showB(){}
virtual void showC(){}
};
/*
内存分配:
B::
vfptr ---->D 0 show
vbptr ---->-4 20
_b
C::
vbptr --->0 12
_c
D:: _d
A::
_a
*/
使用虚基类的内存关系图