C++多重继承的二义性问题
时间:2014-11-09 14:18 点击:次
多重继承可以反映现实生活中的情况,能够有效地处理一些较复杂的问题,使编写程序具有灵活性,但是多重继承也引起了一些值得注意的问题,它增加了程序的复杂度,使 程序的编写和维护变得相对困难,容易出错。其中最常见的问题就是继承的成员同名而产生的二义性(ambiguous)问题。
如果类A和类B中都有成员函数display和数据成员a,类C是类A和类B的直接派生类。分别讨论下列3种情况。
1) 两个基类有同名成员
代码如下所示:
如果在main函数中定义C类对象cl,并调用数据成员a和成员函数display :
C cl;
cl.a=3;
cl.display();
由于基类A和基类B都有数据成员a和成员函数display,编译系统无法判别要访问的是哪一个基类的成员,因此程序编译出错。那么,应该怎样解决这个问题呢?可以用基类名来限定:
cl.A::a=3; //引用cl对象中的基类A的数据成员a
cl.A::display(); //调用cl对象中的基类A的成员函数display
如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以不 必写对象名而直接写
A::a = 3; //指当前对象
A::display();
2) 两个基类和派生类三者都有同名成员
将上面的C类声明改为:
class C: public A, public B
{
int a;
void display();
};
如果在main函数中定义C类对象cl,并调用数据成员a和成员函数display:
C cl;
cl.a = 3;
cl.display();
此时,程序能通过编译,也可以正常运行。请问:执行时访问的是哪一个类中的成员?答案是:访问的是派生类C中的成员。规则是:基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。因此如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。请注意:不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。
有些读者可能对同名覆盖感到不大好理解。为了说明问题,举个例子,例如把中国作为基类,四川则是中国的派生类,成都则是四川的派生类。基类是相对抽象的,派生类是相对具体的,基类处于外层,具有较广泛的作用域,派生类处于内层,具有局部的作用域。若“中国”类中有平均温度这一属性,四川和成都也都有平均温度这一属性,如果没有四川和成都这两个派生类,谈平均温度显然是指全国平均温度。如果在四川,谈论当地的平均温度显然是指四川的平均温度;如果在成都,谈论当地的平均温度显然是指成都的平均温度。这就是说,全国的“平均温度”在四川省被四川的“平均温度”屏蔽了,或者说,四川的“平均温度”在当地屏蔽了全国的“平均温度”。四川人最关心的是四川的温度,当然不希望用全国温度覆盖四川的平均温度。
如果在四川要查全国平均温度,一定要声明:我要查的是全国的平均温度。同样,要在派生类外访问基类A中的成员,应指明作用域A,写成以下形式:
cl.A::a=3; //表示是派生类对象cl中的基类A中的数据成员a
cl.A::display(); //表示是派生类对象cl中的基类A中的成员函数display
3) 类A和类B是从同一个基类派生的
代码如下所示:
在类A和类B中虽然没有定义数据成员a和成员函数display,但是它们分别从类N继承了数据成员a和成员函数display,这样在类A和类B中同时存在着两个同名的数据成员a和成员函数display。它们是N类成员的拷贝。类A和类B中的数据成员a代表两个不同的存储单元,可以分别存放不同的数据。在程序中可以通过类A和类B的构造函数去调用基类N的构造函数,分别对类A和类B的数据成员a初始化。
怎样才能访问类A中从基类N继承下来的成员呢?显然不能用
cl.a = 3; cl.display();
或
cl.N::a = 3; cl. N::display();
因为这样依然无法区别是类A中从基类N继承下来的成员,还是类B中从基类N继承下来的成员。应当通过类N的直接派生类名来指出要访问的是类N的哪一个派生类中的基类成员。如
cl.A::a=3; cl.A::display(); //要访问的是类N的派生类A中的基类成员
如果类A和类B中都有成员函数display和数据成员a,类C是类A和类B的直接派生类。分别讨论下列3种情况。
1) 两个基类有同名成员
代码如下所示:
- class A
- {
- public:
- int a;
- void display();
- };
- class B
- {
- public:
- int a;
- void display ();
- };
- class C: public A, public B
- {
- public:
- int b;
- void show();
- };
class A { public: int a; void display(); }; class B { public: int a; void display (); }; class C: public A, public B { public: int b; void show(); };
如果在main函数中定义C类对象cl,并调用数据成员a和成员函数display :
C cl;
cl.a=3;
cl.display();
由于基类A和基类B都有数据成员a和成员函数display,编译系统无法判别要访问的是哪一个基类的成员,因此程序编译出错。那么,应该怎样解决这个问题呢?可以用基类名来限定:
cl.A::a=3; //引用cl对象中的基类A的数据成员a
cl.A::display(); //调用cl对象中的基类A的成员函数display
如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以不 必写对象名而直接写
A::a = 3; //指当前对象
A::display();
2) 两个基类和派生类三者都有同名成员
将上面的C类声明改为:
class C: public A, public B
{
int a;
void display();
};
如果在main函数中定义C类对象cl,并调用数据成员a和成员函数display:
C cl;
cl.a = 3;
cl.display();
此时,程序能通过编译,也可以正常运行。请问:执行时访问的是哪一个类中的成员?答案是:访问的是派生类C中的成员。规则是:基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。因此如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。请注意:不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。
有些读者可能对同名覆盖感到不大好理解。为了说明问题,举个例子,例如把中国作为基类,四川则是中国的派生类,成都则是四川的派生类。基类是相对抽象的,派生类是相对具体的,基类处于外层,具有较广泛的作用域,派生类处于内层,具有局部的作用域。若“中国”类中有平均温度这一属性,四川和成都也都有平均温度这一属性,如果没有四川和成都这两个派生类,谈平均温度显然是指全国平均温度。如果在四川,谈论当地的平均温度显然是指四川的平均温度;如果在成都,谈论当地的平均温度显然是指成都的平均温度。这就是说,全国的“平均温度”在四川省被四川的“平均温度”屏蔽了,或者说,四川的“平均温度”在当地屏蔽了全国的“平均温度”。四川人最关心的是四川的温度,当然不希望用全国温度覆盖四川的平均温度。
如果在四川要查全国平均温度,一定要声明:我要查的是全国的平均温度。同样,要在派生类外访问基类A中的成员,应指明作用域A,写成以下形式:
cl.A::a=3; //表示是派生类对象cl中的基类A中的数据成员a
cl.A::display(); //表示是派生类对象cl中的基类A中的成员函数display
3) 类A和类B是从同一个基类派生的
代码如下所示:
- class N
- {
- public:
- int a;
- void display(){ cout<<"A::a="<<a<<endl; }
- };
- class A: public N
- {
- public:
- int al;
- };
- class B: public N
- {
- public:
- int a2;
- };
- class C: public A, public B
- {
- public:
- int a3;
- void show(){ cout<<"a3="<<a3<<endl; }
- }
- int main()
- {
- C cl; //定义C类对象cl
- // 其他代码
- }
class N { public: int a; void display(){ cout<<"A::a="<<a<<endl; } }; class A: public N { public: int al; }; class B: public N { public: int a2; }; class C: public A, public B { public: int a3; void show(){ cout<<"a3="<<a3<<endl; } } int main() { C cl; //定义C类对象cl // 其他代码 }
怎样才能访问类A中从基类N继承下来的成员呢?显然不能用
cl.a = 3; cl.display();
或
cl.N::a = 3; cl. N::display();
因为这样依然无法区别是类A中从基类N继承下来的成员,还是类B中从基类N继承下来的成员。应当通过类N的直接派生类名来指出要访问的是类N的哪一个派生类中的基类成员。如
cl.A::a=3; cl.A::display(); //要访问的是类N的派生类A中的基类成员
顶一下
(0)
0%
踩一下
(0)
0%
上一篇:C++类的多重继承
下一篇:C++虚基类详解
相关内容:
最新内容
热点内容
- QQ群
-
微信
- 返回首页
- 返回顶部