派生过程中的“名字覆盖”……

 8.1.1.    派生类与作用域

问题:假设基类B有一个成员函数名为foo,然后B的派生类D也定义了一个同名的foo函数,事情会怎样?

Code:
  1. class B  
  2. {  
  3. public:  
  4.     void foo() { cout << "B::foo" << endl; }          
  5. };  
  6.   
  7. class D : public B  
  8. {  
  9. public:  
  10.     void foo() { cout << "D::foo" << endl; }  
  11. };  

注意:D::fooB::foo同名,并且它们都是类的公开成员。如果我们写这样的代码:

Code:
  1. void test()  
  2. {  
  3.     D d;  
  4.     d.foo(); // <-- 这里调用的是哪个类的foo?  
  5. }  

        这问题简单,确定一定以及肯定……这里调用的是派生类,也就是D类的foo成员函数。还记得在《感受篇》里:“美人类/Beauty”派生自“普通人类(Person)”,然后可以拥有美人特定的“自我介绍函数/Introduction”过程——这背后反应的逻辑是自然而然的:派生类可以改掉基类的某个过程,但如果代码是这样:

Code:
  1. class D : public B  
  2. {  
  3. private// <- 注意这里,foo在派生类里变成私有的  
  4.     void foo() { cout << "D::foo" << endl; }  
  5. };  

基类定义没有变,但派生类中,foo函数被改成私有,此时,若还是这样调用:

Code:
  1. void test()  
  2. {  
  3.     D d;  
  4.     d.foo(); // <- 会怎样?  
  5. }  

结果是编译不通过,因为fooD中是私有的。此时, 尽管在基类里,就有一个公开着的同名foo函数,编译器也会对派生类“从一而终”,决不改弦更张换成对基类同名函数的调用(除非主动要求就要用基类的那个foo)。

〖轻松一刻〗:父亲的狗,儿子的狗,重名了……

这可以打个(又是粗俗)的比喻:

 

故事背景:父亲(基类)有一只名为“黑太狼”的小狗,并且父亲说,这只狗对外公开(谁都可以借);儿子(派生类)继承了老爸。这时来了一个儿子的客人……

情况一:儿子自己根本没有养狗(或者有养,但不叫黑太狼)。客人说:“我想用你那只叫黑太狼的狗”。儿子想:“我又没有这样的狗,噢,对了,老爸那有一只”。于是乎儿子把他从父亲继承而来黑太狼,借给客人一用。

情况二:儿子自己也养了一只狗,并且也把它取名为“黑太狼”。客人说:“我想用你那只叫黑太狼的狗”。儿子很乐意地说:好的,我的这只黑太郎也是public的,你拿走吧!(父亲的黑太郎在一边偷偷地哭泣)。

情况三:儿子自己也养了一只狗,并且也把它取名为“黑太狼”。客人说:“我想用你那只叫黑太狼的狗”。可是儿子认为狗狗是私有的,不应外借,于是乎,儿子道声歉(编译出错信息),拒了客人走掉。父亲的的黑太狼暗自悲伤:“有了新欢也就罢了,偏还要把它和我取一样的名!你是当我死了吗?你是当我死了吗?”

情况四:儿子自己也养了一只狗,并且也把它取名为“黑太狼”。但客人说:“我想借你爸爸那只叫‘黑太狼’的狗狗”。儿子点点头,父亲的“黑太狼”热泪盈眶:“这样的夜晚你是这样特意想起我了”,于是乎它高高兴兴地出场。

当一个派生类搞了一个和基类中存在同名的成员,这个事件被称为:“派生过程的名字覆盖”。事件的影响是:在派生类的世界里,基的那个同名成员突然失效了;哪怕其实基类的那个成员有着比派生类同名成员更宽松的访问权限;哪怕……

没错,还有一个哪怕!并且这个哪怕显然比前一个哪怕还要令人觉得“哪怕”!翠花,上代码:

Code:
  1. class B  
  2. {  
  3. public:  
  4.     void foo() { cout << "B::foo" << endl; }          
  5. };  
  6.   
  7. class D : public B  
  8. {  
  9. private:   

  10.    int foo(int a)  // <- foo在派生类里需要入参     
  11.    { return a + a; }    
  12.   
  13. void test()  
  14. {  
  15.     D d;  
  16.       
  17.     d.foo(); // <- 会怎样?  
  18. }  

现在情况是:派生类D中,虽然也有一个同名的foo函数,可以它却长得基类的那个同名函数完全不一样。然后客人(test函数)来了,它想调用基类的那个foo函数——为什么我能猜出它要的是基类的?因为它没有给foo写任何入参嘛……

〖轻松一刻〗:父亲的狗,儿子的鹦鹉,重名了……

这回是:父亲有一只叫“黑太郎”的狗狗,儿子有一只叫“黑太郎”的……鹦鹉!来了一个客人,手上拿着狗粮,说“哎,把你家那只‘黑太郎’借我一下,你看,我连狗粮都带来了……”不料儿子道歉一声:“不好意思,你所传入的数据,和我家黑太狼所要的入参,不匹配!”

 

这是一种严谨的做法,主要用以预防因为派生类的某些修改,造成程序的行为偷偷地发生了变化(编译通过,但程序逻辑发生变化)。

那么,怎么让代码显式地指明要调基类的同名函数呢?很简单,在所要调用成员之前,加上基类的类名域前缀即可:

Code:
  1. void test()  
  2. {  
  3.     D d;  
  4.       
  5.     d.B::foo(); //明确调用B的foo函数,而不是D的...  
  6. }  

 -------------------------------------------------------------------------------

您理解本文提及的知识吗?,想知道有多少同学和你有同样的理解呢?请参看相关测试题:

 http://student.csdn.net/space.php?do=question&ac=detail&qid=1835

 -------------------------------------------------------------------------------

         如果您想与我交流,请点击如下链接成为我的好友:
         http://student.csdn.net/invite.php?u=112600&c=f635b3cf130f350c

         -------------------------------------------------------------------------------

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南郁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值