这两个技术一般只会在大的项目才会遇到。在多重继承中,派生类对它的每一个基类的成员都会保留一份,在复杂的面向对象建模中,派生类可能直接或间接地继承同一个基类多次,如果不做特别说明的话,派生类就会保留多份这个基类的成员,为防止这种情况的出现,需要将继承同一个基类时候的继承动作声明为虚继承,而这个被多次继承的基类称为虚基类。这里通过一些简单的例子,加以说明:
一、多重继承
#include<iostream>
using namespace std;
class Base1{
public:
Base1(){
cout<<"constru Base1"<<endl;
}
virtual void print(){
cout<<"Base1"<<endl;
}
virtual ~Base1(){
cout<<"del Base1"<<endl;
}
};
class Base2{
public:
Base2(){
cout<<"constru Base2"<<endl;
}
virtual void print(){
cout<<"Base2"<<endl;
}
virtual ~Base2(){
cout<<"del Base2"<<endl;
}
};
class D1:public Base1{
public:
D1(){
cout<<"constru D1"<<endl;
}
virtual void print(){
cout<<"D1"<<endl;
}
virtual ~D1(){
cout<<"del D1"<<endl;
}
};
class D2:public Base2{
public:
D2(){
cout<<"constru D2"<<endl;
}
virtual void print(int i){
cout<<"D2"<<endl;
}
virtual ~D2(){
cout<<"del D2"<<endl;
}
};
class MI:public D1,public D2{
public:
MI(){
cout<<"constru MI"<<endl;
}
virtual void print(){
cout<<"MI"<<endl;
}
virtual ~MI(){
cout<<"del MI"<<endl;
}
};
int main(){
MI mi;
mi.Base1::print(); //如果MI中没有重新定义print函数的话,这里对print的调用会出现二义性,因为
//MI的直接基类D1和D2中都定义了print函数,即使参数不同,但名字查找先与类型检查
//而且这种查找会在MI的两个继承分支中并行进行,只要在两个分支中都找到print,就
//出现二义性错误,不管在继承体系中出现的层次深浅如何。
system("pause");
Base1 *pb1=new MI;
cout<<"*************"<<endl;
Base2 *pb2=new MI;
cout<<"*************"<<endl;
D1 *pd1=new MI;
cout<<"*************"<<endl;
D2 *pd2=new MI;
cout<<"*************"<<endl;
pb1->print();
pd1->print();
pd2->print();
cout<<"*************"<<endl;
delete pb2;
cout<<"*************"<<endl;
delete pd1;
cout<<"*************"<<endl;
delete pd2;
system("pause");
}
通过观察上面程序的运行结果可以发现:在多重继承体系中,对象的析构顺序与构造顺序也是正好相反的,并且多个基类的构造顺序由派生列表中基类出现的顺序决定,而与初始化列表中的构造顺序无关(上面的程序没有给出初始化列表)。
二、虚继承
#include<iostream>
using namespace std;
class B{
public:
void print(){
cout<<"B"<<endl;
}
};
class D1:public virtual B{
};
class D2:public virtual B{
};
class MI:public D1,public D2{
};
int main(){
MI mi;
mi.print();
system("pause");
}
两处的virtual关键字
缺一不可,不然mi.print()语句将报错:对print的调用出现二义性。因为MI继承了B的两份成员。