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

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

原文地址:

Qt Quick 图像处理实例之美图秀秀(附源码下载)

题目是酱紫的:

#include <iostream>
#include <string>
using namespace std;
class A
{
protected:
    int m_data;
public:
    A(int data = 0)
    {
        m_data = data;
    }
    int GetData()
    {
        return doGetData();
    }
    virtual int doGetData()
    {
        return m_data;
    }
};
class B : public A
{
protected:
    int m_data;
public:
    B(int data = 1)
    {
        m_data = data;
    }
    int doGetData()
    {
        return m_data;
    }
};
class C : public B
{
protected:
    int m_data;
public:
    C(int data = 2)
    {
        m_data = data;
    }
};
void main()
{
    C c(10);

    cout << c.GetData() << endl;
    cout << c.A::GetData()<< endl;
    cout << c.B::GetData()<< endl;
    cout << c.C::GetData()<< endl;

    cout << c.doGetData()<< endl;
    cout << c.A::doGetData()<< endl;
    cout << c.B::doGetData()<< endl;
    cout << c.C::doGetData()<< endl;
}

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

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

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

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

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

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

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

不知道说明白了没有?

时间: 2024-08-26 06:30:36

详解一道C++笔试题,考察重载、覆盖、多态的相关文章

java 多态性详解及常见面试题

java多态性 多态分两种: (1)   编译时多态(设计时多态):方法重载. (2)   运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态.(我们平时说得多的事运行时多态,所以多态主要也是指运行时多态) 运行时多态存在的三个必要条件: 一.要有继承(包括接口的实现): 二.要有重写: 三.父类引用指向子类对象. 多态的好处: 1.可替换性(substitutability).多态对已存在代码具有可替换性.例如,多态对圆Circle类工作,对其他任

一道java笔试题

输入一串用空格隔开的数字串,对于数字串的奇数位按升序排序,偶数位按降序排序. 示例输入: 4 6 2 3 6 7 8 1 处理过程: 奇数位:4 2 6 8 升序排序结果: 2 4 6 8 偶数位:6 3 7 1 降序排序结果: 7 6 3 1 结果输出:2 7 4 6 6 3 8 1 demo: import java.util.*; public class Main { public static void main(String args[]) { Scanner scan = new 

从网易的一道多线程笔试题学习wait与notify来控制线程同步

题目 : 有三个线程分别打印A.B.C,请用多线程编程实现,在屏幕上循环打印10次ABCABC… package my.thread.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class PrintThreadExample { public static void main(String[] args) { PrintThreadExample

HTTP协议【详解】——经典面试题

---恢复内容开始--- http请求由三部分组成,分别是:请求行.消息报头.请求正文 HTTP(超文本传输协议)是一个基于请求与响应模式的.无状态的.应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用. 1.常用的HTTP方法有哪些?GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器.POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST

微软一道简单笔试题

题目非常简单,主要是读控制台输入数据有些忘了,所以折腾有点久.写的好像有点复杂,最终还是实现了.... public class MaxFont { int n; int p; int w; int h; public MaxFont(int n,int p,int w,int h){ this.n=n; this.p=p; this.w=w; this.h=h; } public static void main(String[] args) throws Exception { // TOD

一道搜狗笔试题引发的思考

一: 给定一个数组a[N],我们希望构造数组b[N],其中b[i]=a[0]*a[1]*...*a[N-1]/a[i]. 在构造过程: 1.不允许使用除法: 2.要求O(1)空间复杂度和O(n)时间复杂度: 3.除遍历计数器与a[N] b[N]外,不可使用新的变量(包括栈临时变量.对空间和全局静态变量等): void makeArray(int a[],int b[],int len) { int i,j; b[0] = 1; for(i=1;i<len;i++) { b[i] = b[i-1]

一道阿里笔试题

请把以下用于连接字符串的JavaScript代码修改为更高效的方式var htmlString = ‘ < div class=”container” > ’ + ‘ < ul id=”news-list” > ’;for (var i = 0; i < NEWS.length; i++) {htmlString += ‘ < li > < a href=”’ +NEWS[i].LINK + ‘” > +NEWS[i].TITLE + ‘ < /

关于一道游戏笔试题

如何出BOSS. 每两个小时要随机出一个BOSS提供: 随机函数unsigned int random(void), 返回值是一正随机整数, 取当前时间函数unsigned int GetCurTime(void), 返回值是开机到当前的秒数 出BOSS函数NewBoss(void). 题目是,每两个小时必定出一个,但出现的时间随机,做题时间限制十五分钟 我的答案 int number=-1; int count=0; void start(){ } void update(){ if(numb

一道SQL笔试题:使用标准SQL嵌套语句查询选修全部课程的学员姓名和所属单位

S (SNO,SNAME,SDD,SAGE) SNO,SNAME,SDD,SAGE 分别代表学号.学 员姓名.所属单位.学员年龄 C (CNO,CNAME ) CNO,CNAME 分别代表课程编号.课程名称 SC(SNO,CNO,SCORE ) SNO,CNO,SCORE 分别代表学号. 所选修的 课程编号.学习成绩 三个表的数据如下: 问题: 使用标准SQL嵌套语句查询选修全部课程的学员姓名和所属单位 自己写的SQL: SELECT SNO,SDD FROM S WHERE SNO IN( s