多继承引起的二义性

  多重继承可以反映现实生活中的实际情况,能有有效的处理一些较复杂的问题,使编程具有灵活性。但是多重继承也引起了一些值得注意的问题,它增加了程序的复杂度,使程序的编写和维护变得相当困难。其中最常见的问题就是继承成员同名而产生的二义性(Ambiguous)问题。

①调用不同基类的同名成员时可能出现二义性

 1 class A
 2 {
 3     public:
 4         void setA(int a);
 5         int get();
 6     private:
 7         int a;
 8 } ;
 9 class B
10 {
11     public:
12         void setB(int b);
13         int get();
14     private:
15         int b;
16 } ;
17 
18 class C:public A,public B
19 {
20     public:
21         void setC(int c);
22         int getC();
23     private:
24         int c;
25 };

  在执行obj.get();时将是有二义性的。因为类C分别从类A类B继承了两个不同版本的get()成员函数,因此,obj.get();到底调用哪个get()版本,编译器将无从知晓。

对于这种二义性问题,常见有两种解决方法:

(1)使用作用域分辨符::加以消除。

obj.A::get();

obj.B::get();

(2)在类C中也定义成员函数get()函数,则有类C的对象obj访问get()函数obj.get()没有二义性,这是因为当派生类中的成员与基类中的成员重名时,派生类中的同名成员将被调用。

 1 class A
 2 {
 3     public:
 4         void setA(int a);
 5         int get();
 6     private:
 7         int a;
 8 } ;
 9 class B
10 {
11     public:
12         void setB(int b);
13         int get();
14     private:
15         int b;
16 } ;
17 
18 class C:public A,public B
19 {
20     public:
21         void setC(int c);
22         int get();
23                 //此处改为这样    
24     private:
25         int c;
26 };

②访问共同基类的成员时可能出现二义性

  当一个派生类有多个基类,而这些基类又有一个共同的基类时,也就是所谓的菱形继承。这时对这个共同基类中成员的访问可能出现二义性。

 1 class A
 2 {
 3     public:
 4         void disp(); 
 5     private:
 6         int a;
 7 };
 8 
 9 class B1:public A
10 {
11     public:
12         void dispB1();
13     private: 
14         int b1;
15 };
16 class B2:public A
17 {
18     public:
19         void dispB2();
20     private: 
21         int b2;
22 };
23 
24 class C:public B1,public B2
25 {
26     public:
27         void dispC();
28     private: 
29         int c;
30 };

在此类结构下,如果创建类C的对象c1:

C c1;

则下面的两个访问都有二义性:

c1.disp();

c1.A::disp();

  这是因为B1,B2分别从类A中继承了一个disp()成员函数的副本,因此类C中就有了分别从类B1,B2两条不同路线上继承过来的disp()版本,尽管这两个版本的函数完全相同,但是语句“c1.disp();”将使编译器无从知晓到底调用从类B1继承来的disp(),还是调用从类B2继承来的disp(),这就是导致二义性的原因。

语句“c1.A::disp();”产生二义性的道理相同,不过下面的两条调用语句却是正确的:

c1.B1::disp();

c1.B2::disp();

因为通过“B1::”及“B2::”的限定,明确告诉编译器应该调用从哪条路径上继承过来的disp()。

  在一个类中保留间接共同基类 的多份同名成员,虽然有时是必要的,可以在不同的数据成员中分别存放不同的数据,也可以通过构造函数分别对它们进行初始化。但在大多数情况下,这种现象是人们不希望出现的。

因为保留多份数据成员的副本,不仅占用较多的存储空间,还增加了访问这些成员时的困难。而且在实际上,并不需要有多份副本,为此,c++中提供了虚基类(Virtual Base Class)技术来解决这个问题。

关于虚基类方面的内容请跳转至http://www.cnblogs.com/tenjl-exv/p/7625836.html

原文地址:https://www.cnblogs.com/tenjl-exv/p/7625484.html