【C++】虚函数常见问题
图说C++对象模型:对象内存布局详解 - melonstreet - 博客园 (cnblogs.com)
1.虚函数的代价
1)带有虚函数的每个类会产生一个虚函数表,用来存储虚成员函数的指针
2)带有虚函数的每个类都会有一个指向虚函数表的指针
3)不再是内敛函数,因为内敛函数可以在编译阶段进行替代,而虚函数表示等待,在运行阶段才能确定到达采用哪种函数,所以虚函数不是内敛函数
2.那些函数不能是虚函数?
1)构造函数:对象的虚函数表指针需要通过构造函数初始化
2)内联函数:内联函数可以在编译阶段进行函数体的替换,而虚函数需要在运行期间进行确定
3)静态函数:静态函数不属于对象而属于类,因为静态成员函数没有this指针,所以无法访问对象的虚表指针,也就
无法访问类的虚函数表,将静态函数设置成虚函数也就没有任何意义,所以c++语法不支持将静态函数设置成虚函数
4)友元函数:友元函数不属于类,也不能被继承,没有继承特性的函数没有虚函数的说法
5)类外的普通函数:类外普通函数不是类的成员函数,同样不具备继承特性,也就没有虚函数的说法
3.虚函数和纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:必须在继承类中重新声明函数。让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的默认实现。所以类纯虚函数的声明就是在告诉子类的设计者,"你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它"。
引入原因
1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
4.菱形继承的内存结构?如何解决菱形继承存在的问题?
#include <iostream> using namespace std; class A { public: int x; virtual int getx() { return x; } }; class B:public A{}; class C:public A{}; class D:public B,public C{}; int main() { D d; //以下代码不会被任何编译器通过,因为存在菱形继承问题 cout<<d.getx()<<endl; }
1)菱形继承的内存结构:现在有A,B,C,D四个类,B,C分别继承A类,D通过多重继承继承了BC两个类,现在D类中有两个getx(),D类不知道调用哪一个getx()
2)菱形继承的解决办法:虚继承
BC类都用Virtual标注,保证只有一个getx()被创建
#include <iostream> using namespace std; class A { public: int x=5; virtual int getx() { return x; } }; class B:virtual public A{}; class C:virtual public A{}; class D:public B,public C{}; int main() { D d; cout<<d.getx()<<endl; }
5.虚析构函数的作用?父类的析构函数为什么一定要设置成虚函数?
父类虚析构函数就是为了避免内存泄漏,防止子类内存得不到释放造成内存泄漏
1.当父类的析构函数不声明成虚析构函数时,当子类继承父类,父类指针指向子类对象,delete掉父类指针,只会调动父类的析构函数,而不会调用子类的析构函数,从而造成子类对象内存泄漏
2.当父类的析构函数声明成虚析构函数时,当子类继承父类,父类指针指向子类对象,delete掉父类指针,先调动父类的析构函数,然后调用子类的析构函数,不存在子类对象内存泄漏的问题
只要存在继承关系,则父类的析构函数必须定义成虚函数!
6.构造函数和析构函数中为什么不可以调用虚函数?
背景知识:
1.构造子类对象时,首先调用父类构造函数初始化对象的父类部分,在执行父类的构造函数时,对象的子类部分都是未初始化的,实际上此时对象还不是一个子类对象
2.析构子类对象时,先析构子类部分,然后按照构造顺序逆序析构父类部分
所以在运行子类的构造和析构函数时,对象都是不完整的,为了适应这种不完整,编译器视对象类型为当前构造或析构函数所在类的类型,由此造成的结构就是:在父类的构造或析构函数中,会将子类对象当作父类对象看待
在这样的背景下
如果我们在父类的构造或析构函数中调用虚函数,调用的往往是当前类的虚函数,达不到多态的效果,跟普通函数调用没有区别
7.构造函数为什么不能为虚函数?什么情况下析构函数必须为虚函数?
1)因为虚函数表指针必须在构造函数中初始化,所以构造函数不能为虚函数!
2)当存在继承关系时,父类的析构函数必须为虚函数,这样在父类指针指向子类对象,delete父类指针时,子类对象才不会内存泄漏
纯虚函数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异