虚继承
多继承和多重继承导致的问题?
多继承:就是某一个类继承了好几个基类
多重继承:就是类被一层一层的继承
在上面的这幅图中,类B会拷贝一份A的数据,类C会拷贝一份A的数据,那么这时候类D再继承B和C的话,就会在类D中存在两份A的拷贝 。这是绝对不允许的 。一是浪费空间,二是存在二义性
举例如下:
农民工中就会存在两份人的数据 。
虚继承的出现和实现原理 ?
正因为上面所说的问题的出现,为了解决这个问题,提出了虚继承的概念 。 那么它是如何实现的呐?
虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
在这里我们可以对比虚函数的实现原理:他们有相似之处,都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)。
虚基类依旧存在继承类中,只占用存储空间;虚函数不占用存储空间。
虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。
参考博文:
此篇博客有关于虚继承详细的内存分布情况
http://blog.csdn.net/xiejingfa/article/details/48028491
实例讲解实现原理
我以上面的例子来解释一下虚继承的实现原理,其中工人和农民会以虚继承的方式继承人 。
(1)在工人和农民中各自都会有一个虚基类指针与虚基类表
(2)那么他们是如何找到基类中的成员的呐?就是下面这样子的
(3)再来就是农民工以 public 方式继承工人和农民
可见,在这次的继承中并没有将虚基类表继承下来!其实想一下也很简单了,只要有指针指向就行了呗!
(4)补充说明的是:虚基类表存储的是,虚基类相对直接继承类的偏移(农民工并非是虚基类的直接继承类,工人和农民才是)
代码举例:
<1> 不用虚继承时:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Person{
public:
Person(string theColor){
m_strColor = theColor ;
cout << "Person() " << endl ;
}
virtual ~Person(){
cout << "~Person "<< endl ;
}
void printColor(){
cout << m_strColor << endl ;
cout <<"Person ------ printColor "<< endl ;
}
protected:
string m_strColor ;
};
class Farmer :public Person //颜色 blue
{
public:
Farmer(string thename = "jack" ,string color = "blue"):Person("Farmer"+color)
{
m_strName = thename ;
cout << "Farmer()" << endl ;
}
virtual ~Farmer(){
cout << "~Farmer() " << endl ;
}
protected:
string m_strName ;
};
class Worker : public Person // 颜色 red
{
public:
Worker(string thecode = "001" ,string color = "red "):Person("Worker"+color )
{
m_strCode = thecode ;
cout << "Worker() " << endl ;
}
virtual ~Worker(){
cout << "~Worker " << endl ;
}
protected:
string m_strCode ;
};
class FarmerWorker : public Farmer ,public Worker
{
public:
FarmerWorker(string thename,
string thecode ,
string color):Farmer(thename,color),Worker(thecode,color )
{
cout << "FarmerWorker()" << endl ;
}
~FarmerWorker(){
cout << "~FarmerWorker()" << endl ;
}
};
int main(void){
FarmerWorker *p = new FarmerWorker("liushegxi","666","yellow ");
p->Farmer::printColor();
p->Worker::printColor();
delete p ;
p = nullptr ;
return 0 ;
}
执行结果:
可见,它的的确确是有两份”yellow”的!(Person中的数据成员m_strColor 有两份 )
<2> 使用虚继承时:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Person{
public:
Person(string theColor){
m_strColor = theColor ;
cout << "Person() " << endl ;
}
virtual ~Person(){
cout << "~Person "<< endl ;
}
void printColor(){
cout << m_strColor << endl ;
cout <<"Person ------ printColor "<< endl ;
}
protected:
string m_strColor ;
};
class Farmer : virtual public Person //颜色 blue
{
public:
Farmer(string thename = "jack ",string color = "blue " ):Person(color)
{
m_strName = thename ;
cout << "Farmer()" << endl ;
}
virtual ~Farmer(){
cout << "~Farmer() " << endl ;
}
protected:
string m_strName ;
};
class Worker : virtual public Person // 颜色 red
{
public:
Worker(string thecode ="001",string color = "red " ):Person(color )
{
m_strCode = thecode ;
cout << "Worker() " << endl ;
}
virtual ~Worker(){
cout << "~Worker " << endl ;
}
protected:
string m_strCode ;
};
class FarmerWorker : public Farmer ,public Worker
{
public:
FarmerWorker(string thename,
string thecode ,
string the_color):Farmer(thename,the_color),Worker(thecode,the_color),Person(the_color) //注意Person 也得显式的写出来
{
cout << "FarmerWorker()" << endl ;
}
~FarmerWorker(){
cout << "~FarmerWorker()" << endl ;
}
};
int main(void){
FarmerWorker *p = new FarmerWorker("liushegxi","666","yellow ");
p->Farmer::printColor();
p->Worker::printColor();
delete p ;
p = nullptr ;
return 0 ;
}
执行结果:
观察可得,Person 只被实例化了一次,那么它也就只能产生一份数据了,其他类中只有虚基类指针指向它而已。