C++多重继承相关问题

  多重继承的问题,首先想到多继承构造函数如何处理?

  Symbian下是可以一个C类多个M类多重继承的

  C++中的多继承,构造函数处理并没有问题,对象构造的时候按照继承中声明的顺序调用多个父类的构造函数,析构函数同样遵守单继承中的原则。

二意性问题

  如果多基类中存在同名成员,会产生二意性的问题

  比如,Root1类中声明DoAny()接口,Root2类中也声明了DoAny()接口,Child多承继Root1和Root2,那么如果Child对象直接调用DoAny接口就会产生编译错误。

  深入原因??

  对象值给父类指针、引用产生的二义性问题

  继续上面的例子,Child对象的引用可以赋给Root1和Root2类引用。

  如果存在void display(const Root1&),void display(cont Root2&)两个接口,那么Child child;    display(child);这个调用也会产生二义性错误,编译器不知道应该调用哪个接口。

  

名字查找过程

  查找过程是一个域一个域的查找,直到全局域。每个类都有一个类域。看如下示例代码:

class ZooAnimal {
public:
	void print() const;
	// 为了向外域暴露设为 public
	string is_a;
	int ival;
private::
	double dval;
};

class Bear : public ZooAnimal {
public:
	void print() const;
	int mumble( int );
	// 为了向外域暴露设为 public
	string name;
	int ival;
};
Bear bear;    bear.is_a;

  如上调用,is_a的名字解析过程为:

  1. 首先查找Bear类域,没有找到

  2. 查找Bear父类ZooAnimal类域,找到其声明,名字解析成功。

bear.ival;

  这个调用解析过程如何呢?因为Bear和其父类ZooAnimal里都有该成员定义。答案是按照上的顺序查找,查找到马上返回。

  1. Bear类域中查找,找到ival声明,解析成功,不再向下查找。那么父类中名字就被子类中定义屏蔽了。

  需要用bear.ZooAnimal::ival的形式来域限定符访问ZooAnimal类中成员ival,这样编译器直接在ZooAnimal类域中开始查找名字。

看如下调用各名字的位置:

  int ival;

  int Bear::mumble(int ival)

  {

    return  ival + //形参

    ::ival + //全局成员

    ZooAnimal::ival +

    Bear::ival;

  }

而如下名字解析会出错:

  int dval;

  int Bear::mumble(int ival)

  {

    return  ival + dval;//----dval为ZooAnimal私有成员,错误

  }

  解析过程如下:

  1. 在Bear类域中查找,没有找到

  2. 在ZooAnimal类域中查找,找到,但是因为私有成员不能访问,编译错误,不再向下查找。

  3. 因为上一步,全局域中的名字就被屏蔽了。

多继承中的情况

  多继承中,名字的解析的时候,对父类域的查找是同时进行的。同时查找Root1类域和Root2类域,如果发现找到两个同样的名字,那么由于二义性名字解析出错,产生编译错误。


========================更新========================

才现上次的研究还没有抓住多继承问题的重点,下面来一个新的总结:

1.    普通多继承

类同时继承自多个基类就是多继承,C++支持多继承。

但是需要注意多继承产生的二意性问题,如果两个基类都存在同名的成员,那么在子类中对它的访问就是有二义性错误的。名字解析过程中,同时找到两个满足条件的成员,这让编译器无法确定应该使用哪个。

其中比较典型的问题就是下面要讲到的:菱形继承问题。

2.    Private、public、protected继承

2.1  private继承

与public继承的不同:

²  不是父类的子类型,也就是说不能赋值给父类指针或引用。

²  接口私有化。

通常多继承时会使用public继承a,private继承b的方式。

可以通过如下方式还原private基类中的成员访问级别:

public:

         //using private father func

         //You can level down the access level, butcan't level up it.

         using ZooAnimal::GetLegs;//correct

但是,经过我的测试,你可以还原或降低原有的访问级别,不能提高访问级别。比如基类中本身是private方法,在public块中using这个方法是编译错误的。

 

2.2  public继承

普通的继承方式

2.3  protected继承

基类所有public成员成为派生类的protected成员。

3.    菱形继承

3.1 基本概念

菱形继承是指:有基类A,B、C继承自A,D多继承自B、C,于是最终派生类D由两条不同的继承线继承基类A。

这是多继承中典型需要考虑的问题,这种情况下D的实例对基类A成员的访问都是二义性错误的。

3.2 普通继承方式中存在的问题

D对象中存在A类的2份不同的对象。

存储2份A对象浪费空间。

A的构造函数会被调用2次。

引起二义性问题。

因为是2份不同的对象,2个对象中成员的值不一致。

 

为解决如上的种种问题,C++引用虚继承。

4.    虚继承

4.1  基本概念

虚继承的基类叫做虚拟基类,无论它在派生层次中出现多少次,只有一个共享的基类子对象被继承。

C++ Primmer书里不建议大量使用虚继承,会有效率问题。

4.2  构造、析构

虚继承时,需要由最终派生类主动调用虚基类的构造函数,否则编译错误。

因为,对于虚继承的情况编译器会阻止中间派生类调用虚基类的构造函数。

比如:

在类D的实例初始过程中,D为最终派生类,B、C为中间派生类,A为虚基类。

调用D的构造函数前,先找到它的基类B,调用B的构造函数,调用B构造函数前找到B的基类A,发现为虚继承关系,阻止B对A构造函数的调用。编译器会确定D有没有对A构造函数的调用,如没有编译报错。

虚基类的构造函数最先被调用,然后再按声明顺序构造非虚基类。于是构造函数是:A、B、C、D调用顺序。

析构函数是和构造相反的顺序进行。

 

4.3  成员可视性

有如下几种情况:

情况

普通继承

虚继承

A中的接口,未被B、C重载

二义性

OK,调用A的接口

B重载了A的接口,C未重载

二义性

OK,调用B的接口

B、C都重载的接口

二义性

二义性

 





  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值