C++中的虚函数与纯虚函数
文章目录
- 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++类中存在纯虚函数就成为了抽象类。
纯虚函数的语法规则:
能够将父类的析构函数声明为纯虚函数?
可以的,当我们将符类的析构函数声明为纯虚时,仍然需要给出父类析构函数的实现,否则链接报错。此时父类仍然是抽象类,由于有了虚函数,就可以实现多态,使用父类指针释放空间时可以正确的调用析构函数。
参考资料:
- C/C++从入门到精通-高级程序员之路【奇牛学院】
C++中的虚函数与纯虚函数相关推荐
- C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的 ...
- java中所有函数都是虚函数_关于Java:虚拟函数与纯虚函数之间的区别是什么?...
本问题已经有最佳答案,请猛点这里访问. Possible Duplicate: C++ Virtual/Pure Virtual Explained 虚函数和纯虚函数有什么区别? CPP中的纯虚函数与 ...
- C++继承中的普通函数,纯虚函数、虚函数
继承中的普通函数,纯虚函数.虚函数 C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 普通函数(no-virtual) 纯虚函数(pure virtual) 虚函数(impure virtual ...
- c语言中虚函数和纯虚函数,虚函数和纯虚函数的区别是什么?
虚函数和纯虚函数的区别:1.纯虚函数只有定义,没有实现:而虚函数既有定义,也有实现的代码.2.包含纯虚函数的类不能定义其对象,而包含虚函数的则可以. 相关推荐:<C++视频教程> 虚函数( ...
- C++中虚函数、纯虚函数、普通函数三者的区别
转载自:https://www.cnblogs.com/cj2014/p/7692707.html 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态" ...
- 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类
目录 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类): #和 ...
- C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解
C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...
- c语言中虚函数和纯虚函数,C++ 虚函数和纯虚函数的区别
首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...
- C++动态库调用宿主进程中的对象方法《纯虚函数的使用》
可执行程序加载动态库并调用动态库导出的函数是比较容易的: 导入库对应的头文件 在CPP文件中调用函数 在链接程序时加上动态库作为参数 假设demo.cpp中需要用到动态库libadd.so中的某个函数 ...
- 但并不从包含函数声明的接口派生_C++的虚函数和纯虚函数
虚函数:类成员函数前面添加virtual关键字,则该函数被称为虚函数. 纯虚函数:在虚函数的基础上,在函数末尾加上 = 0. class Animal {public: virtual void Sh ...
最新文章
- python让工作自动化_python操作excel让工作自动化
- 数据回发时,维护ASP.NET Tree控件位置
- mfc存储颜色到mysql_mfc存储二进制文件
- php 去年年初和年底时间,PHP 日期与时间
- mysql5.7.21压缩版_mysql5.7.21解压版安装配置图文
- 怎么查看linux硬盘多路径,linux下磁盘多路径
- 透过 ASP.NET 和数据库读写图片
- a59s刷机包卡刷 oppo_OPPO A59st刷机教程_OPPO A59st卡刷升级更新官方系统包
- 华为手机如何投屏到电脑
- 小白如何通过markdown文件自制kindle的 MOBI 格式文档
- 谷歌 地图 android studio,Android Studio百度地图开发(一)
- OSPF的五类LSA概述
- Java CsvReader 读取csv文件
- 联合国 ITU 立项成功,DevOps 标准开启国际化模式!
- 蚂蚁高管被约谈,IPO如何引燃了蚂蚁的监管风波?
- 我的手工制作PCB板
- 机器学习(周志华著)习题 第03章 线性模型
- 聊天室类PHP源码[无名轻聊]
- 【新生请继续猛击】NEW COMER SECOND BLOOD 完全题解及代码
- 中国航空物流行业运行现状与总体布局规划报告2022版