有无虚函数的对比
c++ 中的虚函数用于解决动态多态问题,虚函数的作用是允许在派生类中重新定义与积累同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
首先写两个简单的类,类 b 继承自类 a,即 a 是基类,b 是派生类。
class a{public: void print(){ cout << a << endl; }};class b : public a {public: void print(){ cout << b
另外一点,引用的本质是指针常量,可以认为 a,pa 都指向了 b。( 注意区分常量指针与指针常量,常量指针可以类比于整型指针,即指向一个常量的指针,指针的指向可以修改;指针常量类比于整型常量,即一个指针是个常量,也就是指针只能固定的指向某一单元,指针常量的指向不可改而指向的值可以修改。)
int a, b;int * const p1 = &a; //指针常量const int *p2 = &b; //常量指针 执行函数后,我们发现结果为
因为 a 是 a 类的一个引用,所以 a 的 print( ) 依旧是 a 类的成员函数;pa 是 a 类的指针,同理;而 b 是 b 类的对象,调用的 print( ) 为 b 类的成员函数。简言之就是,没有 virtual 时,调用哪一类的成员函数取决于调用对象 a ,pa,b 在定义时的类型。而此时,若 b 类对象 b 想调用直接基类 a 的 print 函数,则应当 b.a::print( )。
这种 a,pa,b 能调用哪个同名函数在对象定义时已经确定好了的多态,我们称之为静态多态。什么是多态?同一个 print 函数在不同的对象中有不同的作用,这就呈现了多态。
这里再提一点,原本基类指针是用来指向基类对象的,如果用它指向派生类对象,此时基类指针指向的是派生类对象中的基类部分。在没有虚函数时,基类指针是无法调用派生类对象中的成员函数的。
而当我们在 a 类中 print( ) 前加上关键字 virtual,变成虚函数时
class a{public: virtual void print(){ cout << a << endl; }};class b : public a{public: void print(){ cout << b << endl; }}; 再次执行主函数,结果为
这是因为 virtual 跟着对象走,即调用的 print( ) 究竟是 a 类还是 b 类的成员函数取决于“ 调用者 ” a,pa 所指的对象 b 属于哪一类,而不再是取决于 a,pa 本身在定义时的类型了。
这种用基类指针或引用指向某一派生类对象,从而能够调用指针指向的派生类对象中的函数的多态,我们称为动态多态,virtual 正是实现动态多态的关键字。
虚函数表 接着刚才的话题,在 a 类中有虚函数的前提下,我们继续讨论
class c{public: void print(){ cout << c << endl; }};int main(){ cout << sizeof(a): << sizeof(a) << endl; cout << sizeof(b): << sizeof(b) << endl; cout << sizeof(c): << sizeof(c) << endl; a a; b b; c c; cout << sizeof(a): << sizeof(a) << endl; cout << sizeof(b): << sizeof(b) << endl; cout << sizeof(c): << sizeof(c) << endl; return 0;} 执行结果为
为什么有虚函数的 a 类大小为 8 字节,继承 a 的 b 为 8 字节,而没有虚函数的 c 类是 1 字节呢?联想到 64 位操作系统下指针占8个字节内存,而 a 大小也是 8 字节,是巧合吗?
事实上,在包含虚函数的类中,在该类的存储空间中,会有一个指向虚函数表的指针,正是这个指针使 a 的大小变为 8 字节。而指针所指的虚函数表本质上是 a 类中定义的所有虚函数名构成的列表。
a 中只定义了一个虚函数 print( ) ,所以虚函数表中也只有一个虚函数名 print,通过这个虚函数名,再找到整个虚函数 print( ) 在内存中的存储位置 ( b 同理 ) 。
验证虚函数表的存在性 虚函数表看不见摸不着,怎么确定它的存在呢?
int main(){ a a; b b; a.print(); b.print(); cout << ------------------- << endl; typedef void (*func)(); //利用函数指针 func; ((func**)(&a))[0][0](); //((func**)(&a))[0] 代表对象 a 的内存空间中的第一个元素:指向虚函数表的指针; ((func**)(&b))[0][0](); //((func**)(&a))[0][0] 表示虚函数表中第一个函数名; return 0;}
从结果中我们可以发现,((func**)(&a))[0][0](); 等效于 a.print();,即确实证明了对象 a 的内存空间中存有一个指向虚函数表的指针,虚函数表的第一个函数名正是 print 。
你知道bss,data,text,rodata,堆,栈,常量段等区别?
中国传感器与物联网产业联盟落户厦门火炬高新区
芯片“幽灵”漏洞笼罩CES 科技界顶级高管有阴影
国产车规级6核Cortex-A55-芯驰X9
PCB板实物图上的线路走向
C++虚函数virtual详解
比特现金SPV客户端Pixel Wallet可以把比特币现金添加到图片里
TD-LTE频谱规划将提高市场预期
iOS14.2或导致iPhone12系列耗电更严重
51单片机把4位16进制数转成10进制数的方法
PLC该如何为应用程序选择最佳工业控制器
台湾IC设计厂商未能加入供应链 营运面临冲击
国产TWS耳机芯片引爆低端市场需求,智能音箱市场国产芯片份额超五成
什么是单频网技术?单频网技术有哪些优点?实现单频网的难点是什
5G+8K技术超高清Mini/Micro LED显示已成趋势
众多医学影像AI企业开发了相关的医学影像AI产品
菲力尔热成像仪帮助医院找出病人的疼痛
利用ROS控制UR机器人可使用新版操作系统
ISA插槽
如何使用很少的组件制作arduino移动车