详解一道C++笔试题,考察重载、覆盖、多态

本文转载大神博客 转载理由方便自己以后学习。

 C++版看到的,说是面试宝典里的题目,考察重载、覆盖、多态等概念,比较有代表性。今天早上远程辅导 Yan Wang 同学学习 Qt 时还想到了这个题目,如果你能够正确理解这个题目,说明对于 C++ 中的函数重载、覆盖、虚函数、多态等有了正确的认识。然后呢,再来学习 Qt 就顺风顺水了。

    题目是酱紫的:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4. class A  
  5. {  
  6. protected:  
  7.     int m_data;  
  8. public:  
  9.     A(int data = 0)  
  10.     {  
  11.         m_data = data;  
  12.     }  
  13.     int GetData()  
  14.     {  
  15.         return doGetData();  
  16.     }  
  17.     virtual int doGetData()  
  18.     {  
  19.         return m_data;  
  20.     }  
  21. };  
  22. class B : public A  
  23. {  
  24. protected:  
  25.     int m_data;  
  26. public:  
  27.     B(int data = 1)  
  28.     {  
  29.         m_data = data;  
  30.     }  
  31.     int doGetData()  
  32.     {  
  33.         return m_data;  
  34.     }  
  35. };  
  36. class C : public B  
  37. {  
  38. protected:  
  39.     int m_data;  
  40. public:  
  41.     C(int data = 2)  
  42.     {  
  43.         m_data = data;  
  44.     }  
  45. };  
  46. void main()  
  47. {  
  48.     C c(10);  
  49.      
  50.     cout << c.GetData() << endl;  
  51.     cout << c.A::GetData()<< endl;  
  52.     cout << c.B::GetData()<< endl;  
  53.     cout << c.C::GetData()<< endl;  
  54.      
  55.     cout << c.doGetData()<< endl;  
  56.     cout << c.A::doGetData()<< endl;  
  57.     cout << c.B::doGetData()<< endl;  
  58.     cout << c.C::doGetData()<< endl;  
  59. }  

    程序运行后的输出结果是什么?

    答案是:1 1 1 1 1 0 1 1

    追问一下,去掉 A 类中 doGetData() 函数的 virtual 修饰,程序运行后的输出结果是什么?

    答案是:0 0 0 0 1 0 1 1


    说说我对这个题目的理解。

    首先我们从对象的角度来看一下。
    先看 main() 函数中构造 c 对象的语句:
    

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. C c(10);  

    这句产生下面的效果:

  • c.m_data = 10;
  • ((B)c).m_data = 1;
  • ((A)c).m_data = 0;

    为什么呢?

    C c(10); 这行代码,定义了 c 对象,但实际上内存中有三个对象:

  1.     class C 的实例对象,即 c
  2.     由于 C 是 B 的派生类,会产生一个 class B 实例对象,即 (B)c
  3.     由于 B 是 A 的派生类,会产生一个 A 实例对象,即 (A)c

    每个对象都有 m_data 成员变量, 根据继承关系,子类覆盖父类的同名成员(变量、函数), c 对象中实际上有三个同名的 m_data 变量,你通过不同的身份去看,会看到不同的 m_data 。

    C 对象生成时,调用 B 、A 类的构造函数,对 B 、 A 初始化,结果就是:

  •     c 对象的 m_data = 10;
  •     ((B)c) 对象的 m_data = 1; 
  •     ((A)c) 对象的 m_data = 0.

    这就是我们一开始给出的效果。

    明白了对象的关系和 m_data 成员的值。咱们再来看 GetData() 函数。

    GetData() 函数是 class A 的一个普通方法,根据继承关系, B 、 C 类都可以访问 GetData() 方法,因为 B 、 C 没有重写 GetData() ,所以通过 B 、 C 的对象访问 GetData() ,访问的都是 A 的 GetData() 方法。

    在 GetData() 中,调用了 doGetData() ,注意了,这是个虚函数!如果 A 的后裔重写了 doGetData() 函数,那么这里实际上会调用到继承关系上处于最底层的那个类的 doGetData() 函数,这就是虚函数和多态的概念。如你所见, B 重写了 doGetData() 函数,所以当你调用 c.GetData() , B 的 doGetData() 会被调用, ((B)c).m_data 被返回。其实在 Qt 中,这种用法比比皆是。

    好啦,下面分析 main() 函数的输出结果。

    doGetData() 带 virtual 关键字

    c.A::GetData() / c.GetData() / c.B::GetData() / c.C::GetData()

    这些语句,都是调用 A::GetData() ,最终都调用 ((B)c).doGetData(), 就是B::doGetData() ,访问的是 B 的对象,它的 m_data = 1 ,所以 doGetData() 返回 1 ,最终 GetData() 返回 1 。
   
    C 类没有定义 doGetData() 函数,c.doGetData() 等同于 ((B)c).doGetData() , 访问的是 B 的对象,它的 m_data = 1 ,所以 c.doGetData() 结果是 1 。

    c.A::doGetData() ,类域作用符限定了调用 A::doGetData(),访问 A 的对象,返回 ((A)c).m_data = 0 。

    c.B::doGetData() / c.C::doGetData() 最终都是访问 B 对象的 doGetData() 方法, 返回都是 1 。

    所以 doGetData() 带 virtual 关键字,程序输出 1 1 1 1 1 0 1 1 。

    去掉 doGetData() 的 virtual 关键字  

    因为 GetData() 是 A 类的方法,而 doGetData() 不是虚函数, A 类的 GetData() 内调用的 doGetData() 方法只可能是 A 的对象的方法,((A)c).m_data = 0 ,所以前四个cout结果都是 0 。

   
    c.doGetData() ,C 的对象内没有实现这个函数,调用父类的,即 B 的对象的 doGetData() 方法,访问的 m_data 是 ((B)c) 对象的,是 1 。

    c.A::doGetData() ,调用 A 的方法,访问 A 的对象 ((A)c).m_data ,结果是 0 。

    c.B::doGetData() / c.C::doGetData() ,调用 B 的方法,访问 B 的对象 ((B)c).m_data ,结果为 1 。

    所以,最终结果是:0 0 0 0 1 0 1 1


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值