第21章:class hierarchy

一:多重继承:

1:在单个继承下,派生类的指针和引用可以自动转换为基类的指针和引用,对于多重继承也是如此,派生类的指针和引用可以转换为其任意基类的指针和引用。

class A{
/***/
};

class B{
/***/
};

class C:public A,public B{
/***/
};

void print(const A&);
void highlight(B*);

void f(const C& ex)
{
    print(ex);
}

void f(C* p)
{
    highlight(p);
}

不过在多重继承下,遇到二义转换的可能性很大,因为编译器不会试图根据派生类转换来区别基类的转换,转换到每一个基类都一样好。比如

void print(const A&);
void print(const B&);

C ex; //创建类C的对象
print(ex);  //error; ambiguous;

2:当一个类有多个基类时,通过所有基类同时进行名字查找,多重继承的派生类有可能从两个或多个基类中继承同名成员,对该成员不加限定的使用将导致二义性。比如:

class A{
public:
    virtual void draw() const;
/***/
};

class B{
public:
    virtual void draw() const;
/***/
};

class C:public A,public B{
public:
    void f(const C&);
/***/
};

void C::f(const C& ex)
{
    ex.draw(); //error! 因为在C的基类中都查找到了draw()的函数,导致了二义性!
    ex.A::draw(); //okay! 限定了draw()函数的调用;
    ex.B::draw(); //okay!
}

消除这种二义性最好的方法就是在派生类C中定义draw()函数自己的版本:

class C:public A,public B{
public:
    void draw() override;
/***/
}

void C::draw()
{
    A::draw();
    B::draw();
};

C ex;
ex.draw() //okay! 调用类C自己定义的draw()函数。

同时还需要注意的是:对所有基类进行函数查找时,首先发生的是名字查找,这时候如果来自于两个基类的函数虽然形参表不一样,但函数名字一样,依然会导致二义性。只有在找到了没有二义性的名字后才会进行函数匹配来确定找到的函数是否合法。

二:虚继承:

1:在我们上述例子中,类C继承了类A和类B,但如果类A和类B此时都继承了类COM,这此时类COM在类C中出现了两次,显然不合理,此时采用虚继承来解决此类问题,如果类A虚继承了类COM, 类B虚继承了类COM,则虽然类C继承了类A和类B,但此时类COM只在类C中出现一次。代码如下:

class COM{
/***/
};

class A:virtual public COM{
/***/
};

class B:virtual public COM{
/***/
};

class C:public A,public B{   //Okay!
/***/
};

2:假定通过多个派生路径继承名为X的成员,有下列三种可能性:

1. 如果在每一个路径中,X表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例;
2. 如果在某一个路径中,X是虚基类成员,而在另一路径中X是后代派生类的成员,也没有二义性—特定派生类实例的优先级高于共享虚基类的成员
3. 如果沿每个继承路径X表示后代派生类不同的成员,则该成员的直接访问是有二义性的。此时,像非虚多重继承层次一样,这种二义性最好用在派生类中提供覆盖实例的类来解决。

3:通常,每个类只初始化自己的直接基类,在应用于虚基类时,这个初始化策略会失败。如果使用常规规则,就可能会多次初始化虚基类。类将沿着包含该虚基类的每个继承路径初始化。

为了解决这个重复初始化的问题,在虚派生中,由最底层派生类的构造函数初始化虚基类。比如在我们例子中,类C应该要初始化类COM。这种机制处理就造成了麻烦,因为如果类D继承类C,则类D也要初始化类COM,因此虚基类不要过分使用。

虽然由最底层派生类初始化虚基类,但是任何直接或间接继承虚基类的类一般也必须为该基类提供自己的初始化式,只要可以创建虚基类派生类类型的对象,该类就必须初始化自己的虚基类,这些初始化式只有创建中间类型(没有共用该虚基类)的对象时才可以使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值