php网站开发经理招聘,重庆城市,wordpress页脚插件,云服务器怎么样做网站多重继承的原理
多重继承(multiple inheritance)是指从多个直接基类中产生派生类的能力。
多重继承的派生类继承了所有父类的属性。 在面向对象的编程中#xff0c;多重继承意味着一个类可以从多个父类继承属性和方法。 就像你有一杯混合果汁#xff0c;它是由多种水果榨取…多重继承的原理
多重继承(multiple inheritance)是指从多个直接基类中产生派生类的能力。
多重继承的派生类继承了所有父类的属性。 在面向对象的编程中多重继承意味着一个类可以从多个父类继承属性和方法。 就像你有一杯混合果汁它是由多种水果榨取而来的每种水果都为这杯果汁带来了独特的味道和营养。 假设我们有三个类Animal动物、Bird鸟类和Swimmer游泳者。 Animal类有eat()方法Bird类有fly()方法而Swimmer类有swim()方法。 现在我们想要创建一个新的类叫做SeaBird海鸟它既能飞又能游泳还像其他动物一样吃东西。这时候我们就可以使用多重继承来实现这个需求。 多重继承
多重继承的声明格式如下
class 派生类名:访问说明符 基类1名,访问说明符 基类名2...
{
}或者
struct 派生类名:访问说明符 基类1名,访问说明符 基类名2...
{
}
在派生类的派生列表中可以包含多个基类
class A1:public A{...};
class AB:public A,public B{...};
每个基类包含一个可选的访问说明符。一如往常如果访问说明符被忽略掉了则关键字class对应的默认访问说明符是private关键字struct对应的是public。
class A1:A{};
//和class A1:private A{}等价
//和struct A2:private A{}等价struct A2:A{};
//和struct A2:public A{}等价
//和class A1:public A{}等价
和只有一个基类的继承一样多重继承的派生列表也只能包含已经被定义过的类而且这些类不能是final的。
对于派生类能够继承的基类个数C没有进行特殊规定但是在某个给定的派生列表中同一个基类只能出现一次。
class A1:A,A{}//这是错误的
多重继承的派生类从每个基类中继承状态
在多重继承关系中派生类的对象包含有每个基类的子对象。
就是说一个派生类对象不仅包含自己特有的部分还包含它的基类部分
我们举个例子
class A{
int a;
}
class A1:A{
int b;
}
一个A1对象不仅含有它特有的部分变量b,还含有它的基类部分变量a
派生类构造函数初始化所有基类
构造一个派生类的对象将同时构造并初始化它的所有基类子对象。
与从一个基类进行的派生一样多重继承的派生类的构造函数初始值也只能初始化它的直接基类
class A1
{public:A1(int a_):a(a_){}int a;
};
class A2
{
public:int b;A2(int b_):b(b_){}
};
class AA1 :A1,A2
{int c;AA1(int a_,int b_,int c_):A1(a_),A2(b_),c(c_){}//显式地初始化所有基类
};
class A1
{public:A1(int a_):a(a_){}A1(){}int a;
};
class A2
{
public:int b;A2(int b_):b(b_){}
};
class AA1 :A1,A2
{int c;AA1(int a_,int b_,int c_):A2(b_),c(c_){}//隐式的使用A1的默认构造函数来初始化a
}; 多重继承派生类的构造函数
多重继承派生类的构造函数形式与单一继承时的构造函数形式基本相同只是在派生类的构造函数初始化列表中调用多个基类构造函数。
一般形式为
派生类名(形式参数列表):基类名1(基类1构造函数实参列表),基类名2(基类2构造函数实参列表),...,成员对象名1(子对象1属类构造函数实参列表),...,派生类初始化列表
{派生类初始化函数体
}
派生类的构造函数初始值列表将实参分别传递给每个直接基类。
其中基类的构造顺序与派生列表中基类的出现顺序保持一致而与派生类构造函数初始值列表中基类的顺序无关。
其调用顺序是
调用基类构造函数各个基类按定义时的次序先后调用调用成员对象构造函数各个子对象按声明时的次序先后调用执行派生类初始化列表执行派生类初始化函数体
例多重继承举例
#includeiostream
using namespace std; class Base1 {private:int b1;public:Base1() {b10;cout默认构造Base1b1b1endl;}Base1(int i) {b1i;cout构造Base1b1b1endl;}
};class Base2 {private:int b2;public:Base2() {b20;cout默认构造Base2b2b2endl;}Base2(int j) {b2j;cout构造Base2b2b2endl;}
};class Base3 {public:Base3() {cout默认构造Base3endl;}
};class Derive : public Base1,public Base2,public Base3 {private:Base1 memberBase1;Base2 memberBase2;Base3 memberBase3;public:Derive() {cout默认构造函数Derive.endl; }Derive(int a,int b,int c,int d): Base1(a),Base2(b),memberBase1(c),memberBase2(d) {cout构造Derive.endl;}
};int main()
{coutendl创建派生类对象obj1endl;Derive obj1;coutendl创建派生类对象(1,2,3,4)endl;Derive obj2(1,2,3,4);return 0;
} 运行结果
创建派生类对象obj1:
默认构造Base1:b10//基类默认构造函数下面2个也是
默认构造Base2:b20
默认构造Base3:
默认构造Base1:b10//成员对象的构造函数下面2个也是
默认构造Base2:b20
默认构造Base3:
默认构造函数Derive,//派生类默认构造函数创建派生类对象(1.2.3.4):
构造Base1:b11 //基类构造函数下面2个也是
构造Base2:b22
默认构造Base3:
构造Base1:b13 //成员对象的构造函数下面2个也是
构造Base2:b24
默认构造Base3:
构造Derive. //派生类构造函数
继承的构造函数的易错点
在C11新标准中允许派生类从它的一个或几个基类中继承构造函数。
但是如果从多个基类中继承了相同的构造函数(即形参列表完全相同)则程序将产生错误
struct Base1 {Base1() default;Base1(const std::string) {}Base1(std::shared_ptrint) {}
};
struct Base2 {Base2() default;Base2(const std::string) {}Base2(int) {}
};
//错误D1试图从两个基类中都继承D1::Dl(const string)
struct D1 : public Base1, public Base2 {using Base1::Base1; //从Base1继承构造函数using Base2::Base2; // 从Base2继承构造函数
}; 发生错误的原因是D1企图从多个基类中继承了相同的构造函数(即形参列表完全相同)而且它没有定义自己版本的构造函数。
有两种解决办法
第一种方法
如果我们把Base1的第二个构造函数的函数参数改为int就不会有错误了
struct Base1 {Base1() default;Base1 (int) {}Base1(std::shared_ptrint) {}
};
struct Base2 {Base2() default;Base2(const std::string) {}Base2(int) {}
};
//正确
struct D1 : public Base1, public Base2 {using Base1::Base1; //从Base1继承构造函数using Base2::Base2; // 从Base2继承构造函数
}; 第二种方法
如果一个类从它的多个基类中继承了相同的构造函数则这个类必须为该构造函数定义它自己的版本
struct Base1 {Base1() default;Base1 (const std::string) {}Base1(std::shared_ptrint) {}
};
struct Base2 {Base2() default;Base2(const std::string) {}Base2(int) {}
};
//正确
struct D1 : public Base1, public Base2 {using Base1::Base1; //从Base1继承构造函数using Base2::Base2; // 从Base2继承构造函数D1(const std::stringa):Base1(a),Base2(a){}
};
注意不能是不带任何参数的默认构造函数
struct Base1 {Base1() default;Base1 (const std::string) {}Base1(std::shared_ptrint) {}
};
struct Base2 {Base2() default;Base2(const std::string) {}Base2(int) {}
};
//正确
struct D1 : public Base1, public Base2 {using Base1::Base1; //从Base1继承构造函数using Base2::Base2; // 从Base2继承构造函数D1(){}//错误
}; 析构函数与多重继承
和往常一样派生类的析构函数只负责清除派生类本身分配的资源派生类的成员及基类都是自动销毁的。合成的析构函数体为空。
析构函数的调用顺序正好与构造函数相反。
我们可以来验证一番
#includeiostream
using namespace std;
class A1
{public:A1(int a_) :a(a_) { cout A1的构造函数被调用 endl; }A1(){}int a;~A1() { cout A1的析构函数被调用 endl; }
};class A2
{
public:int b;A2(int b_):b(b_) { cout A2的构造函数被调用 endl; }~A2() { cout A2的析构函数被调用endl; }
};
class AA1 :A1,A2
{
public:int c;AA1(int a_,int b_,int c_):A1(a_),A2(b_),c(c_) { cout AA1的构造函数被调用 endl; }~AA1() { cout AA1的析构函数被调用 endl; }
};
int main()
{{AA1 a(1, 2, 3);}
} 事实确实如此 多重继承的派生类的拷贝与移动操作
与只有一个基类的继承一样多重继承的派生类如果定义了自己的拷贝/赋值构造函数和赋值运算符则必须在完整的对象上执行拷贝、移动或赋值操作。
只有当派生类使用的是合成版本的拷贝、移动或赋值成员时才会自动对其基类部分执行这些操作。在合成的拷贝控制成员中每个基类分别使用自己的对应成员隐式地完成构造、赋值或销毁等工作。
例如
class Bear:public Zooanimal{}
class Panda:public Bear,public Endangered{}Panda ying_ yang(ying_yang)
panda ling_ling ying_yang;// 使用拷贝构造函数 将调用Bear的拷贝构造函数后者又在执行自己的迷贝任务之前先调用ZooAnimal的的拷贝构造函数。一旦ling_ling 的Bear 部分构造完成接着就会调用 Endangered来创建对象相应的部分。最后执行Panda的拷贝构造函数。合成的移动构造函数的工作机理与之类似。 合成的拷贝赋值运算符的行为与拷贝构造函数报相似它首先球值Bear 部分(并且通过Bear赋值ZooAnimal部分)然后赋值Endanagered部分最后是Panda部分。
类型转换与多个基类
在只有一个基类的情况下公有继承派生类的指针或引用能自动转换成一个可访问基类的指针或引用。多个基类的情况与之类似。
我们可以令某个可访问基类的指针或引用直接指向一个派生类对象。
例如一个ZooAnimal、Bear或Endangered类型的指针或引用可以绑定到Panda对象上//接受
Panda的基类引用的一系列操作
class Bear:public Zooanimal{}
class Panda:public Bear,public Endangered{}void print(const Bear);
void highlight (const Endangered) ;
ostream operator(ostream, const ZooAnimal);Panda ying yang(ying_yang)
print(ying_yang); //把一个Panda对象传递给一个Bear的引用
highlight(ying_yang); // 把一个 Panda 对象传递给一个Endangered的引用
cout ying yang endl;// 把一个Panda 对象传递给一个ZooAnimal的引用
编译器不会在派生类向基类的几种转换中进行比较和选择因为在它看来转换到任意一种基类都一样好。
例如如果存在如下所示的print重载形式
void print(const Bear);
void print(const Endangered);
则通过Panda对象对不带前缀限定符的print函数进行调用将产生编译错误
Panda ying_yang(ying_yang)
print(ying_yang) // 二义性错误 基于指针类型或引用类型的查找 与只有一个基类的继承一样对象、指针和引用的前态类里快定了我们能够使用哪些成员。 如果我们使用一个ZooAnimal指针则只有定义在zooAnimal中的操作是可以使用的Panda 接口中的 Bear、Panda和Endangered特有的部分都不可见。 类似的一个 Bear 类型的指针或引用只能访问BearzooAnimal的成员一个Endangered的指针或引用只能访问Endangered的成员 举个例子已知我们的类已经定义了表中列出的虚函数考虑下面的这些函数调用。
在ZooAnimal/Endangered中定义的虚函数 函数含有自定义版本的类printZooAnimal::ZooAnimal Bear::Bear Endangered::Endangered Panda::PandahighlightEndangered::Endangered Panda::PandatoesBear::Bear Panda::PandacuddlePanda::Panda析构函数ZooAnimal::ZooAnimal Endangered::Endangered class Bear:public Zooanimal{}
class Panda:public Bear,public Endangered{}Bear *pb new Panda(ying yang)
pb-print() // 正确Panda::print()
pb-cuddle() // 错误不属于Bear的接口
pb-highlight() // 错误不属于Bear的接口
delete pb; // 正确Panda::~Panda()
当我们通过Endangered的指针或引用访问一个Panda 对象时Panda 接口中Panda特有的部分以及属于Bear的部分都是不可见的
class Bear:public Zooanimal{}
class Panda:public Bear,public Endangered{}Endangered *pe new Panda(ying yang);
pe-print(); //正确Panda::print()
pe-toes(); // 错误不属于 Endangered的接口
pe-cuddle() // 错误不属于 Endangered的接口
pe-highlight() // 正确Panda::highlight()
delete pe; // 正确Panda::~Panda()
a 二义性问题及名字支配规则
1.二义性问题 在只有一个基类的情况下派生类的作用域嵌套在直接基类和间接基类的作用域中。查找过程沿着继承体系自底向上进行直到找到所需的名字。派生类的名字将隐藏基类的同名成员。在多重继承的情况下相同的查找过程在所有直接基类中同时进行。如果名字在多个基类中都被找到则对该名字的使用将具有二义性。 当一个类拥有多个基类时有可能出现派生类从两个或更多基类中继承了同名成员的情况。此时不加前缀限定符直接使用该名字将引发二义性。 多重继承时多个基类可能出现同名的成员。在派生类中如果使用一个表达式的含义能解释为可以访问多个基类的成员则这种对基类成员的访问就是不确定的称这种访问具有二义性。C要求派生类对基类成员的访问必须是无二义性的
例如
class A {public:void fun() { couta.funendl; }
};
class B {public:void fun() { coutb.funendl; }void gun() { coutb.gunendl; }
};
class C:public A,public B {public:void gun() { coutc.gunendl; } //重写gun()void hun() { fun(); } //出现二义性
};
int main()
{C c,*pc;return 0;
}
使用成员名限定可以消除二义性例如
//成员名限定消除二义性
c.A::fun();
c.B::fun();
p-A::fun();
p-B::fun(); 基本形式为
对象名.基类名::成员名
对象指针名-基类名::成员名 2.名字支配规则
C对于在不同的作用域声明的名字可见性原则是如果存在两个或多个具有包含关系的作用域外层声明了一个名字而内层没有再次声明相同的名字那么外层名字在内层可见如果在内层声明了相同的名字则外层名字在内层不可见这时称内层名字隐藏或覆盖了外层名字这种现象称为隐藏规则
在类的派生层次结构中基类的成员和派生类新增的成员都具有类作用域二者的作用域是不同的基类在外层派生类在内层
如果派生类声明了一个和基类成员同名的新成员派生的新成员就覆盖了基类同名成员直接使用成员名只能访问到派生类的成员
如果派生类中声明了与基类成员函数同名的新函数即使函数的参数不同从基类继承的同名函数的所有重载形式也都会被覆盖
如果要访问被覆盖的成员就需要使用基类名和作用域限定运算符来限定
派生类D中的名字N覆盖基类B中同名的名字N称为名字支配规则。如果一个名字支配另一个名字则二者之间不存在二义性当选择该名字时使用支配者的名字如
c.gun(); //使用C::gun 如果要使用被支配者的名字则应使用成员名限定例如
c.B::gun(); //使用B::gun 虚基类
C引入虚基类的目的是为了解决多继承时可能出现的冲突问题。当一个类通过多个路径继承了同一个基类时如果不使用虚基类那么在派生类中就会存在多个基类子对象这可能会导致数据重复和二义性的问题。
通过使用虚基类可以确保在派生类中只有一个基类子对象从而避免数据重复和二义性的问题。虚基类的成员在派生类中只有一个副本不会重复出现。
另外虚基类还可以实现多态性能够让派生类对象按照基类的指针或引用进行使用从而提高代码的灵活性和可扩展性。
1.虚基类的定义
虚基类是在派生类定义时指定继承方式时声明的。声明虚基类的一般形式为
class 派生类名: virtual 访问标签 虚基类名... { //类体成员列表
};
还有一种形式是
class 派生类名: 访问标签 virtual 虚基类名... { // 成员列表
};
需要注意为了保证虚基类在派生类中只继承一次应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承我们下面会举例子
例虚基类举例
#includeiostream
using namespace std; class A { //声明为基类Apublic:A(int n) { //A类的构造函数 nvn;coutMember of Aendl;} void fun() {coutfun of Aendl;}private:int nv;
};class B1: virtual public A { //声明A为虚基类 public:B1(int a):A(a) { //B1类的构造函数 coutMember of B1endl;}private:int nv1;
};class B2: virtual public A { //声明A为虚基类public:B2(int a):A(a) { //B2类的构造函数coutMember of B2endl; } private:int nv2;
};class C: public B1,public B2 {public://派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用C(int a):A(a),B1(a),B2(a) {coutMember of Cendl;}void fund() {coutfun of Cendl;}private:int nvd;
};int main()
{C c1(1);c1.fund();c1.fun(); //不会产生二义性return 0;
} 现在C对象只将包含A对象的一个副本。从更本质的说继承的B1和B2对象共享一个A对象而不是各种引入自己的A对象副本。这样子调用A类方法就不会有二义性了不知道调用B!继承的还是B2继承的A类方法。
如果我们不引入虚基类再看看这个例子
#includeiostream
using namespace std;class A { //声明为基类A
public:A(int n) { //A类的构造函数 nv n;cout Member of A endl;}void fun() {cout fun of A endl;}
private:int nv;
};class B1 : virtual public A { //声明A为虚基类
public:B1(int a) :A(a) { //B1类的构造函数 cout Member of B1 endl;}
private:int nv1;
};class B2 : public A { //注意这里没有声明A为虚基类
public:B2(int a) :A(a) { //B2类的构造函数cout Member of B2 endl;}
private:int nv2;
};class C : public B1, public B2 {
public://派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用C(int a) :A(a), B1(a), B2(a) {cout Member of C endl;}void fund() {cout fun of C endl;}
private:int nvd;
};int main()
{C c1(1);c1.fund();c1.fun(); //产生了二义性return 0;
}
我们将B2后面的virtual去掉使A类失去虚基类的性质。发现上面这个程序出现了二义性编译器不知道调用B1继承的A类方法还是B2继承的A类方法。 2.虚基类的初始化
如果在虚基类中定义了带参数的构造函数而且没有定义默认构造函数则在其所有派生类包括直接派生和间接派生中都要通过构造函数的初始化表对虚基类进行初始化。例如
class A { public: A(int) { } }; //定义构造函数有参数的基类
class B: virtual public A {public:B(int a):A(a) { } //对基类A初始化
};
class C: virtual public A {public:C(int a):A(a) { } //对基类A初始化
};
class D: public B,public C {public:D(int a):A(a),B(a),C(a) { }
};
在最后的派生类中不仅要负责对其直接基类进行初始化还要负责对虚基类初始化
关于虚基类的说明
1一个类可以在一个类族中即被用作虚基类也被用作非虚基类
2派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用如果未列出则表示使用该虚基类的默认构造函数
3在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时虚基类的构造函数先于非虚基类的构造函数执行多重继承应用举例
#includeiostream
using namespace std; enum Color { //颜色枚举类型 Red,Yellow,Green,White
};class Circle { //圆类Circle的定义private:float radius;public:Circle(float r) {radiusr;coutCircle initialized!endl;} ~Circle() {coutCircle destroyed!endl;}float Area() {return 3.1415926*radius*radius;}
};class Table { //桌子类Table的定义private:float height;public:Table(float h) {heighth;coutTable initialized!endl;}~Table() {coutTable destroyed!endl;}float Height() {return height;}
};class RoundTable: public Table,public Circle { //圆桌类的定义private:Color color;public:RoundTable(float h,float r,Color c);int GetColor() {return color;}~RoundTable() {coutRoundTable destroyed!endl;}
};RoundTable::RoundTable(float h,float r,Color c):Table(h),Circle(r) { //圆桌构造函数的定义colorc;coutRoundTable initialized!endl;
}int main()
{RoundTable cir_table(15.0,2.0,Yellow);coutThe table properties are:endl;coutHeightcir_table.Height()endl; //调用Table类的成员函数coutAreacir_table.Area()endl; //调用circle类的成员函数coutColorcir_table.GetColor()endl; //调用RoundTable 类的成员函数return 0;
} 运行结果
Table initialized!
Circle initialized!
RoundTable initialized!
The table properties are:
Height15
Area12.5664
Color1
RoundTable destroyed!
Circle destroyed!
Table destroyed! 支持向基类的常规类型转换
不论基类是不是虚基类派生类对象都能被可访问基类的指针或引用操作。
例如下面这些从Panda向基类的类型转换都是合法的
class Raccoon:public virtual ZooAnimal{}
class Bear:virtual public ZooAnimal{}
class Panda:public Bear,public Raccoon,public Endangered{}void dance (const Bear);
void rummage (const Raccoon);
ostream operator(ostream, const ZooAnimal)Panda ying_yang; // 正确把一个Panda对象当成Bear传递
dance(ying_yang)//正确把一个Panda对象当成Raccoon传递
rummage (ying_yang);// 正确把一个 Panda 对象当成 ZooAnimal 传递
cout ying_yang; 虚基类成员的可见性
因为在每个共享的虚基类中只有唯一一个共享的子对象所以该基类的成员可以被直接访问并且不会产生二义性。
此外如果虚基类的成员只被一条派生路径覆盖则我们仍然可以直接访问这个被覆盖的成员。但是如果成员被多于一个基类覆盖则一般情况下派生类必须为该成员自定义一个新的版本。
假定类B定义了一个名为x的成员D1和D2都是从B虚继承得到的D继承了D1和D2则在D的作用域中x通过D的两个基类都是可见的。如果我们通过D的对象使用x有三种可能性
如果在D1和D2中都没有x的定义则x将被解析为B的成员此时不存在二义性一个D的对象只含有x的一个实例。
class B
{
public:int x;B(int x_):x(x_){}B() {}
};
class D1:virtual public B
{
public:D1(int a_):B(a_){}
};
class D2 :virtual public B
{
public:D2(int a_):B(a_) {}
};
class D :public D1, public D2
{public:D(int a1,int a2):B(a1),D1(a1),D2(a2){}
};
int main()
{D a(1, 1);cout a.x endl;//解析为B.x
}
如果x是B的成员同时是D1和D2中某一个的成员则同样没有二义性派生类的x比共享虚基类B的x优先级更高。
class B
{
public:int x;B(int x_):x(x_){}B() {}
};
class D1:virtual public B
{
public:D1(int a_):B(a_){}
};
class D2 :virtual public B
{
public:D2(int a_):B(a_),x(a_) {}int x;//定义了x
};
class D :public D1, public D2
{public:D(int a1,int a2,int a3):B(a1),D1(a2),D2(a3){}
};
int main()
{D a(1, 2,3);cout a.x endl;//结果是3
}
如果在D1和D2中都有x的定义则直接访问x将产生二义性问题。
class B
{
public:int x;B(int x_):x(x_){}B() {}
};
class D1:virtual public B
{
public:D1(int a_) :B(a_), x(a_) {}int x;//定义了x
};
class D2 :virtual public B
{
public:D2(int a_):B(a_),x(a_) {}int x;//定义了x
};
class D :public D1, public D2
{public:D(int a1,int a2,int a3):B(a1),D1(a2),D2(a3){}
};
int main()
{D a(1, 2,3);cout a.x endl;
}
与非虚的多重继承体系一样解决这种二义性问题最好的方法是在派生类中为成员自定义 新的实例。 构造函数与虚继承
在虚派生中虚基类是由最低层的派生类初始化的。
class Raccoon:public virtual ZooAnimal{}
class Bear:virtual public ZooAnimal{}
class Panda:public Bear,public Raccoon,public Endangered{}
以我们的程序为例当创建Panda对象时由Panda的构造函数独自控制zooAnimal的初始化过程。 为了理解这一规则我们不妨假设当以普通规则处理初始化任务时会发生什么情况。 在此例中虚基类将会在多条继承路径上被重复初始化。 以ZooAnimal为例如果应用普通规则则Raccoon和Bear都会试图初始化Panda对象的ZooAnimal部分 当然继承体系中的每个类都可能在某个时刻成为“最低层的派生类”。 只要我们能创建虚基类的派生类对象该派生类的构造函数就必须初始化它的虚基类。
例如在我们的继承体系中当创建一个Bear(或Raccoon)的对象时它已经位于派生的最低层因 此Bear(或Raccoon)的构造函数将直接初始化其ZooAnimal基类部分
class Raccoon:public virtual ZooAnimal{}
class Bear:virtual public ZooAnimal{}
class Panda:public Bear,public Raccoon,public Endangered{}Bear::Bear(std::string name, bool onExhibit)ZooAnimal (name, onExhibit, Bear){}Raccoon::Raccoon(std::string name, bool onExhibit)ZooAnimal (name, onExhibit, Raccoon) {}
而当创建一个Panda 对象时Panda位于派生的最低层并由它负责初始化共享的ZooAnimal基类部分。即使ZooAnimal不是Panda的直接基类Panda的构造函数也可以初始化ZooAnimal
class Raccoon:public virtual ZooAnimal{}
class Bear:virtual public ZooAnimal{}
class Panda:public Bear,public Raccoon,public Endangered{}Panda::Panda(std::string name, bool onExhibit):ZooAnimal (name, onExhibit, Panda),Bear (name, onExhibit)Raccoon(name, onExhibit) Endangered(Endangered::critical)sleeping_flag(false) {}
虚继承的对象的构造方式
含有虚基类的对象的构造顺序与一般的顺序稍有区别:
首先使用提供给最低层派生类构造函数的初始值初始化该对象的虚基类子部分接下来按照直接基类在派生列表中出现的次序依次对其进行初始化。
例如当我们创建一个Panda对象时
首先使用Panda 的构造函数初始值列表中提供的初始值构造虚基类ZooAnimal部分。接下来构造Bear部分。然后构造Raccoon部分。然后构造第三个直接基类Endangered。最后构造Panda部分。
如果Panda 没有显式地初始化ZooAnimal基类则ZooAnimal的默认构造函数将被调用。如果ZooAnimal没有默认构造函数则代码将发生错误。 虚基类总是先于非虚基类构造与它们在继承体系中的求序和位置无关。 构造函数与析构函数的次序
一个类可以有多个虚基类。此时这些虚的子对象按照它们在派生列表中出现的顺序从左向右依次构造。
例如在下面这个稍显杂乱的Teddvpear派生关系中有两个虚基类
ToyAnimal是直接虚基类ZooAnima1是Bear的虚基类
class Character {}
class BookCharacter:public Character{}
class ToyAnimal {};
class TeddyBear : public BookCharacter, public Bear, public virtual ToyAnimal{}
编译器按照直接基类的声明顺序对其依次进行检查以确定其中是否含有虚基类。
如果有则先构造虚基类然后按照声明的顺序逐一构造其他非虚基类。
因此要想创建一个TeddyBear对象需要按照如下次序调用这些构造函数
ZooAnimal() // Bear的虚基类
ToyAnimal(); //直接虚基类
Character() //第一个非虚基类的间接基类
BookCharacter(); // 第一个直接非虚基类
Bear()// 第二个直接非虚基类
TeddyBear() //最低层的派生类
合成的拷贝和移动构造函数按照完全相同的顺序执行合成的赋值运算符中的成员也按照该顺序赋值。
和往常一样对象的销毁顺序与构造顺序正好相反首先销毁TeddyBear 部分最后销毁zooAnimal部分。 文章转载自: http://www.morning.zkzjm.cn.gov.cn.zkzjm.cn http://www.morning.dhxnr.cn.gov.cn.dhxnr.cn http://www.morning.rxfgh.cn.gov.cn.rxfgh.cn http://www.morning.c7497.cn.gov.cn.c7497.cn http://www.morning.jfbpf.cn.gov.cn.jfbpf.cn http://www.morning.hmhdn.cn.gov.cn.hmhdn.cn http://www.morning.flncd.cn.gov.cn.flncd.cn http://www.morning.qfrsm.cn.gov.cn.qfrsm.cn http://www.morning.jhyfb.cn.gov.cn.jhyfb.cn http://www.morning.qkdbz.cn.gov.cn.qkdbz.cn http://www.morning.qpqb.cn.gov.cn.qpqb.cn http://www.morning.drnfc.cn.gov.cn.drnfc.cn http://www.morning.rlksq.cn.gov.cn.rlksq.cn http://www.morning.drhnj.cn.gov.cn.drhnj.cn http://www.morning.fqssx.cn.gov.cn.fqssx.cn http://www.morning.snyqb.cn.gov.cn.snyqb.cn http://www.morning.xqbgm.cn.gov.cn.xqbgm.cn http://www.morning.rmyt.cn.gov.cn.rmyt.cn http://www.morning.lcbgf.cn.gov.cn.lcbgf.cn http://www.morning.hphfy.cn.gov.cn.hphfy.cn http://www.morning.ksjnl.cn.gov.cn.ksjnl.cn http://www.morning.rptdz.cn.gov.cn.rptdz.cn http://www.morning.yuminfo.com.gov.cn.yuminfo.com http://www.morning.lnyds.cn.gov.cn.lnyds.cn http://www.morning.yqwsd.cn.gov.cn.yqwsd.cn http://www.morning.qywfw.cn.gov.cn.qywfw.cn http://www.morning.lhygbh.com.gov.cn.lhygbh.com http://www.morning.fcwxs.cn.gov.cn.fcwxs.cn http://www.morning.slysg.cn.gov.cn.slysg.cn http://www.morning.kgnnc.cn.gov.cn.kgnnc.cn http://www.morning.dwxqf.cn.gov.cn.dwxqf.cn http://www.morning.rwtlj.cn.gov.cn.rwtlj.cn http://www.morning.jnptt.cn.gov.cn.jnptt.cn http://www.morning.srgbr.cn.gov.cn.srgbr.cn http://www.morning.bwmq.cn.gov.cn.bwmq.cn http://www.morning.hpdpp.cn.gov.cn.hpdpp.cn http://www.morning.txqgd.cn.gov.cn.txqgd.cn http://www.morning.stpkz.cn.gov.cn.stpkz.cn http://www.morning.mzwqt.cn.gov.cn.mzwqt.cn http://www.morning.hhnhb.cn.gov.cn.hhnhb.cn http://www.morning.mmjyk.cn.gov.cn.mmjyk.cn http://www.morning.klyzg.cn.gov.cn.klyzg.cn http://www.morning.fqljq.cn.gov.cn.fqljq.cn http://www.morning.spqtq.cn.gov.cn.spqtq.cn http://www.morning.jjsxh.cn.gov.cn.jjsxh.cn http://www.morning.ckdgj.cn.gov.cn.ckdgj.cn http://www.morning.zrwlz.cn.gov.cn.zrwlz.cn http://www.morning.hqsnt.cn.gov.cn.hqsnt.cn http://www.morning.wtnwf.cn.gov.cn.wtnwf.cn http://www.morning.kgphc.cn.gov.cn.kgphc.cn http://www.morning.zwtp.cn.gov.cn.zwtp.cn http://www.morning.ckdgj.cn.gov.cn.ckdgj.cn http://www.morning.kqfdrqb.cn.gov.cn.kqfdrqb.cn http://www.morning.kcsx.cn.gov.cn.kcsx.cn http://www.morning.bzjpn.cn.gov.cn.bzjpn.cn http://www.morning.kgqww.cn.gov.cn.kgqww.cn http://www.morning.pcqxr.cn.gov.cn.pcqxr.cn http://www.morning.rcyrm.cn.gov.cn.rcyrm.cn http://www.morning.nfbkp.cn.gov.cn.nfbkp.cn http://www.morning.pqktp.cn.gov.cn.pqktp.cn http://www.morning.psdsk.cn.gov.cn.psdsk.cn http://www.morning.bcdqf.cn.gov.cn.bcdqf.cn http://www.morning.qkzdc.cn.gov.cn.qkzdc.cn http://www.morning.kngx.cn.gov.cn.kngx.cn http://www.morning.shuanga.com.cn.gov.cn.shuanga.com.cn http://www.morning.lhsdf.cn.gov.cn.lhsdf.cn http://www.morning.tyjp.cn.gov.cn.tyjp.cn http://www.morning.hdqqr.cn.gov.cn.hdqqr.cn http://www.morning.mdfxn.cn.gov.cn.mdfxn.cn http://www.morning.tsnwf.cn.gov.cn.tsnwf.cn http://www.morning.jzfrl.cn.gov.cn.jzfrl.cn http://www.morning.qlznd.cn.gov.cn.qlznd.cn http://www.morning.ptqbt.cn.gov.cn.ptqbt.cn http://www.morning.mcwgn.cn.gov.cn.mcwgn.cn http://www.morning.sxfmg.cn.gov.cn.sxfmg.cn http://www.morning.gzzxlp.com.gov.cn.gzzxlp.com http://www.morning.darwallet.cn.gov.cn.darwallet.cn http://www.morning.hfyll.cn.gov.cn.hfyll.cn http://www.morning.rlksq.cn.gov.cn.rlksq.cn http://www.morning.wgqtt.cn.gov.cn.wgqtt.cn