近来看了侯捷的《深入浅出MFC》,读到C++重要性质中的虚函数与多态那部分内容时,顿时有了疑惑。因为书中说了这么一句:使用“基类之指针”指向“派生类之对象”,由该指针只能调用基类所定义的函数,如果要让基类的指针使用派生类中定义的函数,就将该函数定义为虚函数。

但在“Object slicing与虚函数”这一小节给出了一个及其经典的例子,它指出,在向上(即向基类)强制转型时,会造成对象内容的被切割。

下面用示例进行说明:

#include "stdafx.h"
#include <iostream>
using namespace std;class A
{
public:virtual void fn(){cout<<"A fn"<<endl;}
};class B: public A
{
public:virtual void fn(){cout<<"B fn"<<endl;}
};int main(int argc, char* argv[])
{B b1;A a1=(A)b1;A * a2=(A*)&b1;a1.fn();a2->fn();return 0;
}

结果如下:

通过调试分析其内存模型如下:

可知,通过A  a1=(A)b1传值时,对象中指向虚函数表的指针__vfptr值不同,但是通过A *a2=(A*)&b1传址操作时,对象中指向虚函数表的指针__vfptr值是相同的,调用的是类B对象的虚函数表中虚函数fn()。注意,对象调用的是哪个类的虚函数就要看类对象的虚函数表中的函数是哪一个。这句话也好理解,a1虚函数表中fn()是A::fn(),a2虚函数表中fn()是B::fn(),所以出现以上结果。

在如下包含父类的父类的继承中:

class A
{
public:
virtual void fn(){cout<<"A fn"<<endl;}
};

class B: public A
{
public:
virtual void fn(){cout<<"B fn"<<endl;}
};

class C: public B
{
public:

};

C c;
A a1=(C)c;
A * a2=(C*)&c;
a1.fn();
a2->fn();

这时,a1虚函数表中fn()为A::fn(),a2虚函数表中fn()为B::fn(),因为类C对象的虚函数表中fn()为B::fn(),a2中指向虚函数表的指针值与类C对象指向虚函数表的指针值相同。

侯捷的书中有句话是这么对它进行解释的:由于(A)b1.fn()是传值而非传地址操作,编译器以所谓的拷贝构造函数把A对象内容复制了一份,使得b1的虚函数表内容与A对象的虚函数表内容相同。

看来,得多读圣贤书啊,读懂读透啊!

C++ 虚函数在基类与派生类对象间的表现及其分析相关推荐

  1. 深剖基类和派生类的虚函数表

    1.当派生类实现基类的虚函数时,基类中虚函数表和虚函数地址和派生类中虚函数表和虚函数地址不同: 当派生类不实现基类的虚函数时,基类中虚函数表和虚函数地址和派生类中虚函数表和虚函数的地址相同. 1.派生 ...

  2. c++ 基类和派生类的虚函数表是否为同一个

    总结 派生类实现基类的虚函数时,基类中虚函数表和派生类的虚函数表地址不同,基类虚函数表中的虚函数地址和派生类虚函数表中的虚函数地址不同: 派生类不实现基类的虚函数时,基类中虚函数表和派生类中虚函数表地 ...

  3. 基类、派生类、虚基类、虚函数、虚析构、纯虚函数、抽象类

    基类:被其它类通过继承方式作为父类继承的类称为基类:描述派生类的统一种类的方式. 派生类:通过继承其他类(并可能含有自定义成员)实现的子类:为提高代码的重用性及与同样继承于同一个父类的其它类形成统一种 ...

  4. 构造函数怎么在主函数调用_C++ 虚基类及其派生类构造函数(学习笔记:第7章 12)...

    虚基类及其派生类构造函数[1] 建立对象时所指定的类称为最远派生类. 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的. 在整个继承结构中,直接或间接继承虚基类的所有派生类,都 ...

  5. 设计一个程序,求正方形和长方形的周长,具体要求如下: (1) 定义正方形类Square作为基类,包含数据成员边长,以及构造函数、求正方形周长的虚函数、输出函数。 (2) 定义类Square的共有派

    设计一个程序,求正方形和长方形的周长,具体要求如下: (1) 定义正方形类Square作为基类,包含数据成员边长,以及构造函数.求正方形周长的虚函数.输出函数. (2) 定义类Square的共有派生类 ...

  6. [C++] - 纯虚函数 抽象基类 接口类

    翻译自:https://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-inter ...

  7. 派生类到基类的转换 和基类到派生类的转换

    一. 基类与派生类的转换     3种继承方式(公用.保护.私有继承)中,公用派生类才是基类真正的子类型,它完整地继承了基类的功能.     不同类型数据之间在一定条件下可以进行类型的转换.基类与派生 ...

  8. C++学习 十五、类继承(1)基类,派生类,访问权限,protected

    C++学习 十五.类继承(1)基类,派生类 前言 类继承 类的关系与继承 基类, 派生类 基类 派生类 构造函数,析构函数 文件位置 访问权限 protected 后记 前言 本篇开始学习C++类的继 ...

  9. 关于C++编程vs2017 error: c2228的一种可能,或称基类位于派生类之后会出现的问题

    上次C++实验编程遇到了一次error: c2228的问题,百度过了也没有答案,最终调换了基类和派生类的顺序才得到解决. 下面是产生异常的一段代码(以上省略基类Plane和其纯虚函数area和girt ...

最新文章

  1. 【C++】C++11 STL算法(九):番外篇
  2. AI一分钟|美团确认收购摩拜;特斯拉今年第一季度产量创历史新高
  3. 算法竞赛入门经典_6数据结构基础
  4. 优秀的Android资源
  5. 不相交集类及其应用生成迷宫
  6. 会计基础第二次模拟题(3)
  7. [转载] 第一个Python CGI编程和配置
  8. (Sublime Text 3)完美替换 GAMS 难用的编辑器
  9. 数组中常用几种的Arrays方法
  10. catia 快捷键 激活零件_CATIA常用快捷键
  11. [AHK]二维数组总结
  12. 2018最新游戏蛮牛Egret游戏引擎
  13. 一个不用背单词的高效英文学习法
  14. 打工不如当老板,注册公司费用和步骤
  15. 如何管理计算机软件,驱动人生怎么管理软件 让你轻松管理电脑中的程序
  16. LeetCode——89.格雷编码
  17. Vue项目——文章发布和修改
  18. 和平精英服务器响应超时什么意思,和平精英服务器无响应,和平精英服务器超时...
  19. python 腾讯视频签到_Python处理腾讯视频
  20. Revit二次开发-根据两个点创建剖面视图

热门文章

  1. 将批注用于类型化 DataSet (摘自MSDN)
  2. 后端_Laravel
  3. 64位系统下使用ODP.NET 11g的异常
  4. 使用 Windows 命令行删除结果
  5. Colocation Guard公司再增1万平方英尺的数据中心空间
  6. 启用Windows 7/2008 R2 XPS Viewer
  7. iptables防火墙的基本配置
  8. freemarker include 和 import
  9. Android 仿PhotoShop调色板应用(三) 主体界面绘制
  10. 关于php插件pdo_mysql的安装