文章目录

  • 1 C++中的虚函数
    • 1.1 虚函数
    • 1.2 单个类的虚函数表
    • 1.3 使用继承的虚函数表
    • 1.4 多重继承的虚函数表
  • 2 C++中的纯虚函数

1 C++中的虚函数

1.1 虚函数

虚函数的定义:

  • 在函数的返回类型之前使用virtual。
  • 只在成员函数的声明中添加virtual, 在成员函数的实现中不要加virtual。

虚函数的继承:

  • 如果某个成员函数被声明为虚函数,那么它的子类【派生类】,以及子类的子类中,所继承的这个成员函数,也自动是虚函数。
  • 如果在子类中重写这个虚函数,可以不用再写virtual, 但是仍建议写virtual, 更可读!

C++中通过虚函数实现多态。

1.2 单个类的虚函数表

代码如下:

#include <iostream>
using namespace std;class Father {public:virtual void func1() { cout << "Father::func1" << endl; }virtual void func2() { cout << "Father::func2" << endl; }virtual void func3() { cout << "Father::func3" << endl; }void func4() { cout << "非虚函数:Father::func4" << endl; }
public:  //为了便于测试,特别该用publicint x = 100;int y = 200;static int z;
};typedef void (*func_t)(void);
int Father::z = 1;
int main(void) {Father father;// 含有虚函数的对象的内存中,最先存储的就是“虚函数表”cout << "对象地址:" << (int*)&father << endl;int* vptr = (int*)*(int*)&father;cout << "虚函数表指针vptr:" << vptr << endl;cout << "调用第1个虚函数: ";((func_t) * (vptr + 0))();cout << "调用第2个虚函数:";((func_t) * (vptr + 1))();cout << "调用第3个虚函数: ";((func_t) * (vptr + 2))();cout << "第1个数据成员的地址: " << endl;cout <<  &father.x << endl;cout << std::hex << (int)&father + 4 << endl;cout << "第1个数据成员的值:" << endl;cout << std::dec <<  father.x << endl;cout << *(int*)((int)&father + 4) << endl;cout << "第2个数据成员的地址: " << endl;cout << &father.y << endl;cout << std::hex << (int)&father + 8 << endl;cout << "第2个数据成员的值:" << endl;cout << std::dec << father.y << endl;cout << *(int*)((int)&father + 8) << endl;cout << "sizeof(father)==" << sizeof(father) << endl;Father father2;cout << "father的虚函数表:";cout << *(int*)(*(int*)&father) << endl;cout << "father2的虚函数表:";cout << *(int*)(*(int*)&father2) << endl;system("pause");return 0;
}

执行效果:

VS的对象内存分布分析:
项目的命令行配置中添加: /d1 reportSingleClassLayoutFather


手绘内存分布:

结果分析:

  • 对象内,首先存储的是“虚函数表指针”,又称“虚表指针”。然后再存储非静态数据成员。
  • 对象的非虚函数,保存在类的代码中!
  • 对象的内存,只存储虚函数表和数据成员(类的静态数据成员,保存在数据区中,和对象是分开存储的)。
  • 添加虚函数后,对象的内存空间不变!仅虚函数表中添加条目。多个对象,共享同一个虚函数表!

1.3 使用继承的虚函数表

首先看如下示例代码:

#include <iostream>
using namespace std;class Father {public:virtual void func1() { cout << "Father::func1" << endl; }virtual void func2() { cout << "Father::func2" << endl; }virtual void func3() { cout << "Father::func3" << endl; }void func4() { cout << "非虚函数:Father::func4" << endl; }
public:  //为了便于测试,特别该用publicint x = 100;int y = 200;
};class Son : public Father {public:void func1() { cout << "Son::func1" << endl; }virtual void func5() { cout << "Son::func5" << endl; }
};typedef void (*func_t)(void);int main(void) {Father father;Son  son;// 含有虚函数的对象的内存中,最先存储的就是“虚函数表”cout << "son对象地址:" << (int*)&son << endl;int* vptr = (int*)*(int*)&son;cout << "虚函数表指针vptr:" << vptr << endl;for (int i = 0; i < 4; i++) {cout << "调用第" << i + 1 << "个虚函数:";((func_t) * (vptr + i))();}for (int i = 0; i < 2; i++) {// +4 是因为先存储了虚表指针cout << *(int*)((int)&son + 4 + i * 4) << endl;}system("pause");return 0;
}

执行效果:

内存分布:

子类虚函数表的构建过程:

1.4 多重继承的虚函数表

代码如下:

#include <iostream>using namespace std;class Father {public:virtual void func1() { cout << "Father::func1" << endl; }virtual void func2() { cout << "Father::func2" << endl; }virtual void func3() { cout << "Father::func3" << endl; }void func4() { cout << "非虚函数:Father::func4" << endl; }
public:int x = 200;int y = 300;static int z;
};class Mother {public:virtual void handle1() { cout << "Mother::handle1" << endl; }virtual void handle2() { cout << "Mother::handle2" << endl; }virtual void handle3() { cout << "Mother::handle3" << endl; }
public: //为了便于测试,使用public权限int m = 400;int n = 500;
};class Son : public Father, public Mother {public:void func1() { cout << "Son::func1" << endl; }virtual void handle1() { cout << "Son::handle1" << endl; }virtual void func5() { cout << "Son::func5" << endl; }
};int Father::z = 0;typedef void(*func_t)(void);int main(void) {Son son;int* vptr = (int*) * (int*)&son;cout << "第一个虚函数表指针:" << vptr << endl;for (int i = 0; i < 4; i++) {cout << "调用第" << i + 1 << "个虚函数:";((func_t) * (vptr + i))();}for (int i = 0; i < 2; i++) {cout << *(int*)((int)&son + 4 + i * 4) << endl;}int* vptr2 = (int*) * ((int*)&son + 3);for (int i = 0; i < 3; i++) {cout << "调用第" << i + 1 << "个虚函数:";((func_t) * (vptr2 + i))();}for (int i = 0; i < 2; i++) {cout << *(int*)((int)&son + 16 + i * 4) << endl;}system("pause");return 0;
}

执行结果:

VS分析:

内存分布:


2 C++中的纯虚函数

纯虚函数是指只定义原型的成员函数。一个C++类中存在纯虚函数就成为了抽象类。

纯虚函数的语法规则:

能够将父类的析构函数声明为纯虚函数?
可以的,当我们将符类的析构函数声明为纯虚时,仍然需要给出父类析构函数的实现,否则链接报错。此时父类仍然是抽象类,由于有了虚函数,就可以实现多态,使用父类指针释放空间时可以正确的调用析构函数。


参考资料:

  1. C/C++从入门到精通-高级程序员之路【奇牛学院】

C++中的虚函数与纯虚函数相关推荐

  1. C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

    C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的 ...

  2. java中所有函数都是虚函数_关于Java:虚拟函数与纯虚函数之间的区别是什么?...

    本问题已经有最佳答案,请猛点这里访问. Possible Duplicate: C++ Virtual/Pure Virtual Explained 虚函数和纯虚函数有什么区别? CPP中的纯虚函数与 ...

  3. C++继承中的普通函数,纯虚函数、虚函数

    继承中的普通函数,纯虚函数.虚函数 C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 普通函数(no-virtual) 纯虚函数(pure virtual) 虚函数(impure virtual ...

  4. c语言中虚函数和纯虚函数,虚函数和纯虚函数的区别是什么?

    虚函数和纯虚函数的区别:1.纯虚函数只有定义,没有实现:而虚函数既有定义,也有实现的代码.2.包含纯虚函数的类不能定义其对象,而包含虚函数的则可以. 相关推荐:<C++视频教程> 虚函数( ...

  5. C++中虚函数、纯虚函数、普通函数三者的区别

    转载自:https://www.cnblogs.com/cj2014/p/7692707.html 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态" ...

  6. 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类

    目录 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类): #和 ...

  7. C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解

    C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...

  8. c语言中虚函数和纯虚函数,C++ 虚函数和纯虚函数的区别

    首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...

  9. C++动态库调用宿主进程中的对象方法《纯虚函数的使用》

    可执行程序加载动态库并调用动态库导出的函数是比较容易的: 导入库对应的头文件 在CPP文件中调用函数 在链接程序时加上动态库作为参数 假设demo.cpp中需要用到动态库libadd.so中的某个函数 ...

  10. 但并不从包含函数声明的接口派生_C++的虚函数和纯虚函数

    虚函数:类成员函数前面添加virtual关键字,则该函数被称为虚函数. 纯虚函数:在虚函数的基础上,在函数末尾加上 = 0. class Animal {public: virtual void Sh ...

最新文章

  1. python让工作自动化_python操作excel让工作自动化
  2. 数据回发时,维护ASP.NET Tree控件位置
  3. mfc存储颜色到mysql_mfc存储二进制文件
  4. php 去年年初和年底时间,PHP 日期与时间
  5. mysql5.7.21压缩版_mysql5.7.21解压版安装配置图文
  6. 怎么查看linux硬盘多路径,linux下磁盘多路径
  7. 透过 ASP.NET 和数据库读写图片
  8. a59s刷机包卡刷 oppo_OPPO A59st刷机教程_OPPO A59st卡刷升级更新官方系统包
  9. 华为手机如何投屏到电脑
  10. 小白如何通过markdown文件自制kindle的 MOBI 格式文档
  11. 谷歌 地图 android studio,Android Studio百度地图开发(一)
  12. OSPF的五类LSA概述
  13. Java CsvReader 读取csv文件
  14. 联合国 ITU 立项成功,DevOps 标准开启国际化模式!
  15. 蚂蚁高管被约谈,IPO如何引燃了蚂蚁的监管风波?
  16. 我的手工制作PCB板
  17. 机器学习(周志华著)习题 第03章 线性模型
  18. 聊天室类PHP源码[无名轻聊]
  19. 【新生请继续猛击】NEW COMER SECOND BLOOD 完全题解及代码
  20. 中国航空物流行业运行现状与总体布局规划报告2022版

热门文章

  1. git 文件全部标红_git冲突解决,代码冲突、合并冲突。【IDEA版本】
  2. 2.3 利用正规化解决过拟合问题-机器学习笔记-斯坦福吴恩达教授
  3. Altium designer不显示飞线的三种方法
  4. 前端javascript实现二进制读写操作
  5. C语言基础:时间转换成字符串 strftime的代码
  6. Linux学习8之Shell编程--基础正则表达式
  7. WPF-3D动效-文字球形环绕
  8. Winform开发框架里面使用事务操作的原理及介绍
  9. PHP-sftp文件上传
  10. 微服务部署:蓝绿部署、滚动部署、灰度发布等部署方案对比与总结