组合
类以另一个类对象作为数据成员的操作,称为组合,当两个类具有包含关系的时候,组合就比继承更能满足我们的要求,在思考如何选择组合与继承的时候,就应该分析两个类之间的关系,组合的实现方式如以下代码片段
代码语言:javascript复制class vehicle{
//..
};
class engine{
//..
};
class car:public vehicle{//public继承vehicle类
public:
engine eng;//与engine类对象组合
};
void vehicleFn(vehicle& v);
void engineFn(engine& e);
int main()
{
car c;
vehicleFn(c);//ok,car类是vehicle的继承
engine(c);//error,参数要求是engine类对象的引用,对象c属于car类,car类并没有继承engine类,仅仅是与engine类对象组合
engine(c.eng);//ok,eng是engine类对象
return 0;
}
继承#2
多继承的模糊性
当一个类继承多个类的时候,如何有两个或以上的类具有同名的数据成员或成员函数,那么将会出现模糊性问题,访问成员时,究竟应该访问哪一个类的成员?
代码语言:javascript复制class gamePlayer{
public:
setWeight(int i){weight=i;}
private:
weight;
//..
};
class tool{
public:
setWeight(int i){weight=i;}
private:
weight;
//..
}:
class computer:public gamePlayer,public tool{
public:
//..
};
int main()
{
computer com;
com.setWeight(20);//setWeight()操作的是gamePlayer还是tool的weight???
return 0;
}
以上操作导致了名称冲突(name collision),编译器禁止。
代码语言:javascript复制int main()
{
computer com;
com.gamePlayer.setWeight(20);//ok,对gamePlayer的weight操作
return 0;
}
虚拟继承
以上案例中,computer(电脑)可以作为游戏机(gamePlayer)也可以作为工具(tool)来使用,所以我们将 computer 类继承于 gamePlayer 和 tool 类,但一台计算机只会存在一个质量,并不存在 gamePlayer 和 tool 两种质量,如果使用传统继承,会是以下情况:
代码语言:javascript复制class Electronics{
//..
protected:
weight;
};
class tool:public Electronics{//计算机作为工具属于电子产品
//..
};
class gamePlayer:public Electronics{//计算机作为游戏设备属于电子产品
//..
};
class computer:public gamePlayer,public tool{
//..
};
computer 继承于 gamePlayer 和 tool,而 gamePlayer 和 tool 又都继承于 Electronics,故 computer 包含一个完整的 gamePlayer 和 tool,而 computer 的子对象 分别都有自己的 Electronics 部分,导致 一个 computer 包含两个 Electronics,这又会导致模糊性产生,计算机并不清楚你的操作是针对于哪一个 Electronics,我们只希望有一个 Electronics 拷贝,同时又要共享 gamePlayer 和 tool 的成员,C 将实现这种继承结构的方法成为虚拟继承(virtual inheritance)。
实现方法很简单,只需要在 gamePlayer 和 tool 继承 Electronics 中加上 virtual 关键字。
此操作会判断是否存在 Electronics 类,如果有,则使用现有的,如果没有则创建一个拷贝。
有了虚拟继承,之前存在的模糊性将不再模糊,因为始终只存在一个 Electronics。
多继承的构造顺序
按从上到下的顺序进行构造:
- 虚拟基类的构造函数按照被继承的顺序进行构造;
- 非虚拟基类的构造函数按照被继承的顺序进行构造;
- 成员对象(组合)的构造函数按照声明顺序进行构造;
- 类本身的构造函数;
如以下代码片段
代码语言:javascript复制class obj1{
public:
obj1(){cout<<"obj1"<<endl;}
};
class obj2{
public:
obj2(){cout<<"obj2"<<endl;}
};
class b1{
public:
b1(){cout<<"b1"<<endl;}
};
class b2{
public:
b2(){cout<<"b2"<<endl;}
};
class b3{
public:
b3(){cout<<"b3"<<endl;}
};
class b4{
public:
b4(){cout<<"b4"<<endl;}
};
class derived:public b1,virtual public b2,public b3,virtual public b4{
public:
derived():b2(),b4(),b1(),b3(),a(),b(){
cout<<"it's ok"<<endl;
}
protected:
obj1 a;
obj2 b;
};
int main()
{
derived test;
cout<<"main function ok"<<endl;
return 0;
}
运行结果:
代码语言:javascript复制b2
b4
b1
b3
obj1
obj2
it's ok
main function ok
虚拟继承的 b2,b4 首先构造,普通继承的 b1,b3 随后,作为数据成员的对象再构造,最后是 derived 类本身的构造函数。
编辑:Henry 2021-03-08 未授权禁止转载
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。