C++虚函数有什么用 C++多态实现原理与代码演示【进阶】
发布时间 - 2026-01-29 00:00:00 点击率:次虚函数实现运行时多态,通过vtable和vptr支持动态绑定;非静态成员函数可声明为虚,构造函数不可,析构函数应为virtual以防资源泄漏;纯虚函数使类抽象化。
虚函数是用来让基类指针/引用调用子类重写函数的
没有 virtual,编译器在编译时就决定调用哪个函数(静态绑定);加上 virtual 后,实际调用哪个函数由运行时对象的真实类型决定(动态绑定)。这是实现运行时多态的唯一语言机制。
常见错误现象:Base* p = new Derived(); p->func(); 调用的却是 Base::func() —— 很可能因为 func() 在基类里没声明为 virtual。
- 只有非静态成员函数能是虚函数;构造函数不能是虚函数,析构函数建议声明为
virtual - 虚函数可以被继承、被重写(override),但重写时签名必须完全一致(C++11 起推荐加
override关键字显式声明) - 纯虚函数(
virtual void func() = 0;)让类变成抽象类,不能实例化,强制子类实现
虚函数表(vtable)和虚函数指针(vptr)是多态的底层支撑
C++ 标准不规定实现细节,但主流编译器(GCC、Clang、MSVC)都用「每个类一张虚函数表 + 每个对象一个虚函数指针」的方式。vtable 是函数指针数组,存着该类所有虚函数的地址;vptr 是对象内存布局开头的一个隐式指针,指向其所属类的 vtable。
使用场景:调试时查看对象内存布局、理解多继承下虚函数调用开销、排查虚函数未生效问题。
性能影响:virtual 函数调用比普通函数多一次间接寻址(通过 vptr 找 vtable,再索引函数指针),但现代 CPU 分支预测和缓存优化后开销极小;真正代价在于禁止内联(除非编译器能确定具体类型)。
示例(简化示意):
struct Base {
virtual void f() { }
virtual void g() { }
};
struct Derived
: Base {
void f() override { } // 覆盖 Base::f
};
// sizeof(Derived) 通常比 sizeof(Base) 大?不一定——vptr 通常只占一个指针大小,且 Base 和 Derived 共享同一份 vptr 偏移
为什么父类析构函数要声明为 virtual
否则 delete 基类指针时,只会调用基类析构函数,子类析构逻辑被跳过 —— 这是资源泄漏(如文件句柄、堆内存、锁)的常见根源。
错误代码:
class Base {
public:
~Base() { cout << "Base dtor\n"; }
};
class Derived : public Base {
int* data;
public:
Derived() { data = new int[100]; }
~Derived() { delete[] data; cout << "Derived dtor\n"; }
};
Base* p = new Derived();
delete p; // 只输出 "Base dtor",data 泄漏
修复方式:把 Base::~Base() 改成 virtual ~Base()。
- 只要类设计为被继承(尤其有虚函数),就该把析构函数设为
virtual - 如果类明确禁止继承,可加
final;若既不继承也不多态,析构函数无需virtual -
std::unique_ptr同样受此规则约束 —— 它的默认删除器也依赖虚析构
虚函数和 override / final 的配合容易踩坑
不加 override 时,子类函数名拼错、参数类型不匹配(如 int vs const int&)、const 限定不一致,都会导致“看似重写实则新增一个重载函数”,多态失效却无编译错误。
示例:
struct Base {
virtual void process(int x);
};
struct Derived : Base {
void process(int x) const override; // 错!const 不匹配,编译失败(有 override)
void process(int x) override; // 对
void process(double x) override; // 错!参数类型不同,不是重写,编译失败
};
- 始终对重写函数加
override—— 编译器会严格校验是否真能覆盖基类虚函数 -
final可用于函数(禁止进一步重写)或类(禁止继承),例如virtual void f() final; - 虚函数不能是
static、friend或template(但虚函数模板特化可以存在)
虚函数机制本身简单,但它的行为高度依赖编译器对继承关系、函数签名、内存布局的精确理解;一旦某个环节出偏差(比如忘记 virtual、签名差一个 const、多继承下 vtable 偏移错乱),多态就静默失效,而这类 bug 往往在运行时才暴露,且难以追踪。
# c++
# 为什么
# Static
# 多态
# 成员函数
# 父类
# 子类
# 构造函数
# 析构函数
# const
# 引用调用
# int
# void
# 指针
# 继承
# 多继承
# 重载函数
# 虚函数
# 纯虚函数
# 堆
# 函数模板
# delete
# 对象
# bug
# 重写
# 这是
# 绑定
# 不匹配
# 特化
# 也不
# 却是
# 句柄
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
C++用Dijkstra(迪杰斯特拉)算法求最短路径
个人摄影网站制作流程,摄影爱好者都去什么网站?
EditPlus中的正则表达式实战(5)
INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】
如何快速搭建高效香港服务器网站?
Laravel怎么在Controller之外的地方验证数据
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
laravel怎么为应用开启和关闭维护模式_laravel应用维护模式开启与关闭方法
网站建设保证美观性,需要考虑的几点问题!
Laravel如何处理CORS跨域请求?(配置示例)
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】
Laravel如何处理JSON字段_Eloquent原生JSON字段类型操作教程
b2c电商网站制作流程,b2c水平综合的电商平台?
如何在Windows虚拟主机上快速搭建网站?
如何在IIS7中新建站点?详细步骤解析
Laravel PHP版本要求一览_Laravel各版本环境要求对照
如何在Windows环境下新建FTP站点并设置权限?
如何获取免费开源的自助建站系统源码?
如何在万网自助建站平台快速创建网站?
SQL查询语句优化的实用方法总结
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
简单实现jsp分页
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】
HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】
Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】
EditPlus中的正则表达式 实战(1)
JavaScript实现Fly Bird小游戏
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?
用v-html解决Vue.js渲染中html标签不被解析的问题
linux写shell需要注意的问题(必看)
免费视频制作网站,更新又快又好的免费电影网站?
HTML透明颜色代码怎么让图片透明_给img元素加透明色的技巧【方法】
Laravel如何发送系统通知?(Notification渠道示例)
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
在线制作视频网站免费,都有哪些好的动漫网站?
实例解析Array和String方法
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】
Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件
Laravel如何实现模型的全局作用域?(Global Scope示例)
如何在服务器上配置二级域名建站?
Linux网络带宽限制_tc配置实践解析【教程】
微信小程序 wx.uploadFile无法上传解决办法
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解


