01
—
c++虚函数探索
c++是一门面向对象语言,在c++里运行时多态是由虚函数和纯虚函数实现的,现在我们看下在c++中如何用虚函数实现多态。先来看一段代码。
// virtual_function.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。 // #include 《iostream》 class base { public: base()
{ std::cout 《《 “base::constructor run” 《《 std::endl; } virtual void fun1()
{ std::cout 《《 “base::fun1 run” 《《 std::endl; } virtual void fun2() { std::cout 《《 “base::fun2 run” 《《 std::endl; } virtual ~base()
{ std::cout 《《 “base::desconstructor run” 《《 std::endl; } }; class derive : public base { public: derive() { std::cout 《《 “derive::constructor run” 《《 std::endl; } void fun1() { std::cout 《《 “derive::fun1 run” 《《 std::endl; } void fun3()
{ std::cout 《《 “derive::fun3 run” 《《 std::endl; } ~derive() { std::cout 《《 “derive::desconstructor run” 《《 std::endl; } }; int main() { derive* d = new derive(); d-》fun1(); d-》fun2(); d-》fun3(); delete d; }
这段代码编译运行后输出了:
base::constructor run derive::constructor run derive::fun1 run base::fun2 run derive::fun3 run derive::desconstructor run base::desconstructor run
这段代码里基类base定义了虚函数fun1和fun2,派生类derive有成员函数fun1和fun3,其中派生类覆盖了继承而来的基类虚函数fun1。在主函数里创建derive类型对象指针d指向derive类型对象。由于派生类derive成员函数fun1覆盖了基类base成员函数fun1,因此通过d调用fun1实际调用的是派生类derive类的成员函数fun1,而继承而来的成员函数fun2没有被覆盖,则通过指针d调用fun2实际调用的是基类成员函数fun2。这里好像让看不出虚函数有什么作用,那么我们将主函数修改如下:
int main() { base* b = new derive(); b-》fun1(); b-》fun2(); delete b; }
在程序里我们将创建一个基类指针b并指向的是派生类,并且调用delete释放内存时使用的是基类指针b。编译运行输出结果如下:
base::constructor run derive::constructor run derive::fun1 run base::fun2 run derive::desconstructor run base::desconstructor run
通过基类指针b调用fun1函数,实际调用的是派生类derive的成员函数fun1,由于在派生类derive中没有定义成员函数fun2,因此通过基类指针b调用fun2函数,实际调用的依旧是基类base的成员函数fun2。代码里虽然我们没有对派生类的成员函数fun1加virtual,实际上派生类的成员函数fun1是虚函数。但是对于大多数c++初学者会有2个疑问的地方。1、通过基类指针b调用fun1函数,实际调用的是派生类的成员函数fun1。2、通过delete释放内存使用的是基类指针b,会调用派生类析构函数和基类析构函数,成功释放内存,不会存在内存泄露问题。
带有虚函数的类称为虚基类,子类继承虚基类。在c++中虚基类有一个虚函数表指针保存虚函数表地址,而虚函数表保存函数地址,虚函数表并不在虚基类里,但是虚函数表指针在虚基类里,子类继承虚基类,子类也就有了虚函数表指针。那么c++是如何通过虚函数表和虚函数表指针实现多态呢?打开vs2019,并用管理员身份运行“2019开发人员命令提示符”工具,如下图所示:
输入:cl /d1 reportsingleclasslayoutxxx [filename],xxx表示类名,[filename]表示类所在的.cpp文件路径。这里我输入源文件的派生类名和源文件路径,回车输出如下:
从输出可以看出派生类从基类继承了虚函数表指针vfptr,且占用字节数大小是4字节,刚好就是一个指针占用字节数。虚函数表vftable里保存了派生类成员函数fun1,基类成员函数fun2的地址,由于派生类成员函数fun3不是虚函数,因此虚函数表里没有fun3的地址。看到这里我们就明白了,通过基类指针b调用fun1的过程:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到派生类成员函数fun1的地址,调用派生类成员函数fun1。而通过基类指针b调用fun2的过程则是:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到基类成员函数fun2的地址,调用基类成员函数fun2。看到这里,第一个疑问已经解开了,关键在于虚函数表指针和虚函数表。
在c++中有虚函数的类,其析构函数默认是虚析构函数,只要是虚函数就会在虚函数表里有相应的函数地址,因此派生类里的虚函数表指针vfptr指向的虚函数表vftable必然保存着派生类析构函数的地址,类的析构过程:从继承链的最底端到最顶端依次调用析构函数,因此delete b调用过程:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到派生类析构函数地址,调用析构函数。
合扬智能卡解说数字人民币“可视卡”生产核心技术
滴滴将与NVIDIA合作共同开发自动驾驶技术
PLC控制系统与电网如何连接
自动驾驶还有多久能实现L5?自动驾驶领域的热门趋势
8Gbps及以上高速信号PCB布线,要注意哪些事?
在C++中如何用虚函数实现多态
华为mate10什么时候上市?华为mate10最新消息:华为p10内存门问题让华为陷入信任危机,华为mate10箭在弦上
无刷电机技术演进与高速风筒行业现状【其利天下高速风筒PCBA方案】
新型智慧城市现在的发展和建设分别处于什么阶段
Intel退役三款五代酷睿处理器:全为第一代的14nm酷睿处理器
京微齐力荣获“硬核中国芯-2022年度最具潜力IC设计企业”奖
小米5月21日发布全新的小爱音箱
射频识别技术漫谈(23)——ISO15693的载波、调制与编码
音圈模组3D打印文琴初试啼声助力“唱响春天”
纸张表面缺陷检测系统的详细介绍
行星减速机型号该怎么选择?顺力电机
雷达液位计厂家经验分享
芯原股份与趣戴科技推动智能手表GUI解决方案创新
使用电量计IC能监测锂离子电池的充电状态
智能酒店智慧互动电视系统具有哪些特点