C++:继承#2与组合

2021-08-09 16:48:32 浏览数 (1)

组合

类以另一个类对象作为数据成员的操作,称为组合,当两个类具有包含关系的时候,组合就比继承更能满足我们的要求,在思考如何选择组合与继承的时候,就应该分析两个类之间的关系,组合的实现方式如以下代码片段

代码语言: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。

多继承的构造顺序

按从上到下的顺序进行构造:

  1. 虚拟基类的构造函数按照被继承的顺序进行构造;
  2. 非虚拟基类的构造函数按照被继承的顺序进行构造;
  3. 成员对象(组合)的构造函数按照声明顺序进行构造;
  4. 类本身的构造函数;

如以下代码片段

代码语言: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 国际许可协议进行许可。

0 人点赞