我们从上篇已经了解到多继承的特性,同时在很多场合中他的负面作用大于它的正面作用。这里是一个简化后的棱形问题,有时,我们不得不使用这样的继承模式.如下图所示
![](https://img.haomeiwen.com/i16148197/96b917325de872b8.png)
为了避免在初始化过程中,顶层的父类重复构造,那么C++有个virtual关键字,在本示例中,我们只需给BlackMagic类和WhiteMagic类的继承列表中分别用virtual关键字修饰父类Magic
#include <string>
#include <iostream>
using namespace std;
class Magic{
public:
Magic(){cout<<"初始化 - Magic实例"<<endl;}
~Magic(){cout<<"内存释放 - Magic实例"<<endl;}
};
class BlackMagic:virtual public Magic{
public:
BlackMagic(){cout<<"初始化-黑魔法师"<<endl;}
~BlackMagic(){cout<<"内存回收-黑魔法师"<<endl;}
....
};
class WhiteMagic:virtual public Magic{
public:
WhiteMagic(){cout<<"初始化-白魔法师"<<endl;}
~WhiteMagic(){cout<<"内存回收-白魔法师"<<endl;}
....
};
class Role:public WhiteMagic,public BlackMagic{
string d_name;
size_t d_hp;
public:
Role(const string& name,
size_t hp):d_name(name),d_hp(hp){
cout<<"角色初始化 -"<<d_name
<<"HP:"<<d_hp<<endl;
}
~Role(){cout<<"内存回收-游戏角色"<<endl;}
size_t attck(){
cout<<d_name<<"施加attack方法"<<endl;
}
size_t magic(){
cout<<d_name<<"施加magic方法"<<endl;
}
};
int main(void){
Role yuna=Role("Yuna",1500);
yuna.fira();
yuna.holy();
return 0;
}
程序输出
![](https://img.haomeiwen.com/i16148197/98fd00331c6f8a45.png)
virtual关键字的副作用,即使父类显式调用了自定义的构造函数,默认情况下也只会调用父类的默认构造函数。那有方法可以显示调用父类的自定义构造函数吗?请继续往下看,这也是本文的重点。
多继承链中调用自定义构造函数
我们对上文的示例代码做一些扩展,Role的继承链中前端的所有父类都各自增加自己的自定义构造函数。
继承链中的子类对上一级父类的自定义构造函数的调用的方法:就是在当前子类构造函数的初始化参数列表中显式调用父类的构造函数,通常,不允许直接调用父类中的构造函数,而必须通过父类进行调用。 仅在使用“virtual”关键字时才允许使用。一般形式如下图所示
![](https://img.haomeiwen.com/i16148197/06c4d195107a534c.png)
继承链中没virtual关键字的情况下,在调用自定义构造函数需要在子类构造函数的初始化参数列表以这样格式"父类名::构造函数原型"显式指定。
![](https://img.haomeiwen.com/i16148197/a484de3703dc6b7a.png)
示例代码
class Magic{
size_t d_mp;
string d_name;
public:
Magic(){cout<<"初始化 - Magic实例"<<endl;}
//父类中构造函数
Magic(const string& name,size_t mp)
:
d_name(name),
d_mp(mp)
{
cout<<"魔法技能:"<<d_name<<"初始MP "<<d_mp<<endl;
}
~Magic(){cout<<"内存释放 - Magic实例"<<endl;}
};
class BlackMagic:virtual public Magic{
public:
BlackMagic(){cout<<"初始化-黑魔法师"<<endl;}
//父类中构造函数
BlackMagic(size_t mp):Magic("黑魔法师",mp){
cout<<"构造函数:BlackMagic("<<mp<<")初始化"<<endl;
}
~BlackMagic(){cout<<"内存回收-黑魔法师"<<endl;}
size_t fira(){
cout<<"fire初级火魔法"<<endl;
}
...
};
class WhiteMagic:virtual public Magic{
public:
WhiteMagic(){cout<<"初始化-白魔法师"<<endl;}
//父类中构造函数
WhiteMagic(size_t mp):Magic("白魔法师",mp){
cout<<"构造函数:WhiteMagic("<<mp<<")初始化"<<endl;
}
~WhiteMagic(){cout<<"内存回收-白魔法师"<<endl;}
size_t haste(){
cout<<"haste魔法"<<endl;
}
...
};
class Role:public WhiteMagic,public BlackMagic{
string d_name;
size_t d_hp;
public:
Role(const string& name,
size_t hp):d_name(name),d_hp(hp){
cout<<"角色初始化 -"<<d_name
<<"HP:"<<d_hp<<endl;
}
Role(const string& name,size_t hp,size_t mp)
:WhiteMagic(mp),BlackMagic(mp),d_name(name),d_hp(hp)
{
cout<<"角色初始化 - "<<d_name<<
"HP:"<<d_hp<<" MP:"<<mp<<endl;
}
~Role(){cout<<"内存回收-游戏角色"<<endl;}
};
int main(void){
Role yuna=Role("Yuna",1500,300);
return 0;
}
程序输出如下:
![](https://img.haomeiwen.com/i16148197/ba3abf9de1c4631c.png)
总结:
关于C++的类继承的部分,我们更多地阐述了一个多继承的副作用,尤其在棱形问题方面在内存分配方面会造成资源浪费,内存回收时,如果继承链中的类内部存在堆内存分配,因为重复调用父类的解构函数,就可能存在内存泄漏的风险。虽然可以在棱形的继承链中的子类使用virtual关键修正这一问题。但virtual关键字也带来一些副作用,我们也逐一阐述了在多继承中如何显式地调用自定义构造函数。
后记
我们在本篇引入了虚拟函数这个概念。这是在C++运行时(Run-Time)多态中一个非常重要的特征。之前写过的很多的文章介绍过函数重载和operator 函数重载。它们属于编译时多态(Compile-Time)。
网友评论