C++面向对象的继承特性:多继承二义性问题、虚继承

1.多继承的概念

多继承就是一个子类继承自多个父类。

在继承中,子类会继承父类的成员变量和成员函数:

class A	
{
    
    
public:
	int a;
	void print(void){
    
    cout<<"a = "<<this->a<<endl;}
};

class C:public A 
{
    
    };

int main(int argc,char**argv)
{
    
    
	C c;
	c.a=5;
	c.print();
	return 0;
}

输出:

a = 5

上面是单继承的情况,C类很好继承了A类的print()方法,多继承和单继承的原理、效果并无明显区别,但是多继承会导致二义性问题,来看这样一种情况,假如C类继承自A类和B类,且B类中也有一个print()方法,这个时候C该继承哪一个呢?

不妨写代码测试一下:

class A
{
    
    
public:
	int a;
	void print(void){
    
    cout<<"a = "<<this->a<<endl;}
};

class B
{
    
    
public:
	int b;
	void print(void){
    
    cout<<"b = "<<this->b<<endl;}
};

class C:public A,public B
{
    
    
	
};

int main(int argc,char**argv)
{
    
    
	C c;
	c.a=5;
	c.print();
	return 0;
}

结果是编译报错了,对于编译器来说,print()是模糊有歧义的:

main.cpp:98:4: error: request for member ‘print’ is ambiguous
  c.print();

多继承的二义性问题有2种情况。

2.多继承的二义性问题情况1

场景1:C多继承自A和B,则C中调用A和B的同名成员时会有二义性。

原因分析:C从A和B各自继承了一个同名(但是不同namespace域)成员,所以用C的对象来调用时编译器无法确定到底想调用的是哪一个。

解决办法:

  • ①避免出现同名方法,让A和B的public成员命名不要重复冲突。但这个有时不可控,比如A和B类都是别人提供的,当另外的人使用C类想继承A,B两个类的时候,还得去找提供这个两个类的人,你俩谁行行好,把print()重命名一下。
  • ②编码时明确指定要调用哪一个,用c.A::print()明确指定调用的是A的print()而不是B的,这就类似于2个人叫张三,在外面高的就叫大张三,矮的叫小张三。
  • ③在C中重定义print(),则调用时会调用C中的print(),A和B中的都被隐藏了。

总结:能解决,但是都没有很好的解决。

3.多继承的二义性问题情况2

场景2:菱形继承问题。即A为祖类,B1:A, B2:A, C:B1,B2,此时用C的对象调用A中的某个方法时会有二义性。

原因分析:c.print()有二义性,c.A::print()也有二义性,但是c.B1::print()c.B2::print()却没有二义性。

解决办法:和情况1中的一样,但是情况2更隐蔽,也更难以避免。

4.多继承的二义性问题情况总结

二义性就是歧义,好的情况表现为编译错误,不好的情况表现为运行时错误,最惨的情况表现为运行时莫名其妙。随着系统规模的变大和逻辑变复杂,难免出现二义性,这是系统自身带来的。

解决二义性问题不能靠写代码的人个人的细心和调试能力,而要靠机制,也就是编程语言的更高级语法特性。虚函数、虚继承、纯虚函数、抽象类、重写覆盖、多态等概念就是干这些事的。

5.虚继承解决菱形继承的二义性问题

5.1 虚继承怎么用

场景:菱形继承导致二义性问题,本质上是在孙子类C中有B1和B2中包含的2份A对象,所以有了二义性。

虚继承解决方案:让B1和B2虚继承A,C再正常多继承B1和B2即可。

class A
{
    
    
public:
	int a;
	void print(void){
    
    cout<<"a = "<<this->a<<endl;}
};

class B1:virtual public A
{
    
    
	//A::print()
};

class B2:virtual public A
{
    
    
	//A::print()
};

class C:public B1,public B2
{
    
    
	//B1::A::print()
	//B2::A::print()
};

虚继承就这么简单,就是为了解决菱形继承的二义性问题而生,和虚函数(为了实现多态特性)并没有直接关系。

5.2 虚继承的实现原理

虚继承的原理是:虚基类表指针vbptr和虚基类表virtual table

猜你喜欢

转载自blog.csdn.net/PecoHe/article/details/113307845