C++中如何实现多态?(虚函数表与动态绑定)
发布时间 - 2026-01-23 00:00:00 点击率:次只有被virtual显式修饰的成员函数,且通过指针或引用调用时,才触发动态绑定;普通函数、静态函数、构造函数及非virtual析构函数均不参与虚函数机制。
虚函数怎么声明才触发动态绑定
只有被 virtual 显式修饰的成员函数,且通过指针或引用调用时,才会走运行时动态绑定。普通函数、静态成员函数、构造函数、析构函数(除非显式声明为 virtual)都不参与虚函数机制。
常见错误是忘记在基类中加 virtual,比如写成 void func() {} 而不是 virtual void func() {},此时即使派生类重写,调用也仍是静态绑定,编译器直接绑定到指针/引用的静态类型所对应的函数。
实操建议:
- 基类中所有预期被重写的接口,一律加上
virtual - 派生类中用
override显式标注重写(C++11 起),避免拼写错误或签名不匹配导致意外隐藏而非重写 - 纯虚函数写作
virtual void func() = 0;,含纯虚函数的类即为抽象类,不能实例化
虚函数表(vtable)在内存

每个含虚函数的类(或其子类)在编译期生成一张全局只读的函数指针表,即 vtable;每个该类的对象开头隐式插入一个指针 vptr,指向其所属类的 vtable。对象布局通常是:vptr + 成员变量。
注意:vtable 不是每个对象一份,而是每类一份;vptr 才是每个对象一份。多继承下可能有多个 vptr(如菱形继承需 virtual 继承来解决)。
典型布局示例(单继承):
class Base {
public:
virtual void f() { cout << "Base::f"; }
int x;
};
class Derived : public Base {
public:
void f() override { cout << "Derived::f"; }
int y;
};
// sizeof(Derived) 通常是 16(x + y + vptr,假设指针占 8 字节)
调试时可通过打印对象地址和 *reinterpret_cast 查看 vptr 指向的 vtable 内容(需关闭优化,且依赖 ABI,仅作理解参考)。
为什么构造函数和析构函数不能是虚函数(除了析构函数例外)
构造函数不能是虚函数,是因为对象尚未完成构造,vptr 还没被初始化到最终类的 vtable —— 它在构造过程中会随继承链逐级更新:先调基类构造,设基类 vtable 地址;再调派生类构造,覆盖为派生类 vtable 地址。此时若允许虚调用,会调到不完整状态的函数,语义危险。
析构函数可以且**应该**是 virtual 的(尤其基类有虚函数时),否则通过基类指针 delete 派生类对象,只会调基类析构,派生部分内存泄漏。
关键点:
- 基类析构函数必须声明为
virtual,哪怕函数体为空 - 一旦类设计为多态基类(即有
virtual函数),就默认应有virtual析构函数 - 不要在构造/析构函数中调用虚函数 —— 此时动态绑定失效,实际调用的是当前正在构造/析构的那个类的版本
动态绑定失败的三个典型场景
即使写了 virtual 和 override,仍可能因调用方式不对而退化为静态绑定。
最容易忽略的是:通过对象值(非指针/引用)调用虚函数,例如 Base b = Derived(); b.func(); —— 发生对象切片(slicing),b 是纯 Base 对象,vptr 指向 Base vtable,调用 Base::func。
其他常见陷阱:
- 使用
static_cast强转指针类型后调用,如static_cast,若(ptr)->func() ptr实际是Derived*,但强制按Base*解释,仍能正确动态绑定;但若强转为无关类型(如Other*),则未定义行为 - 函数参数是值传递而非引用/指针,传入派生类对象会切片,丢失虚函数信息
- 模板函数内直接调用
t.func()(t是模板参数),编译期绑定,与虚函数无关
虚函数机制只对“指针或引用 + 成员函数调用”这一特定语法生效,其余都是普通函数调用。这点必须刻进本能。
# c++
# 多态
# 成员变量
# 成员函数
# 子类
# 构造函数
# 析构函数
# 引用调用
# void
# 指针
# 继承
# 多继承
# 虚函数
# 纯虚函数
# 接口
# 指针类型
# 值传递
# 切片
# delete
# 对象
# 绑定
# 重写
# 派生类
# 的是
# 或引用
# 而非
# 类中
# 都是
# 这一
# 是因为
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Python并发异常传播_错误处理解析【教程】
如何获取PHP WAP自助建站系统源码?
iOS UIView常见属性方法小结
教你用AI将一段旋律扩展成一首完整的曲子
Android使用GridView实现日历的简单功能
如何挑选高效建站主机与优质域名?
Android自定义控件实现温度旋转按钮效果
Laravel PHP版本要求一览_Laravel各版本环境要求对照
清除minerd进程的简单方法
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
,怎么在广州志愿者网站注册?
详解vue.js组件化开发实践
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
Laravel如何处理文件下载请求?(Response示例)
如何在云主机快速搭建网站站点?
英语简历制作免费网站推荐,如何将简历翻译成英文?
Laravel如何操作JSON类型的数据库字段?(Eloquent示例)
如何在搬瓦工VPS快速搭建网站?
如何快速搭建高效服务器建站系统?
悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】
如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框
小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?
Laravel Fortify是什么,和Jetstream有什么关系
logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?
如何在Windows虚拟主机上快速搭建网站?
免费视频制作网站,更新又快又好的免费电影网站?
微信小程序 五星评分(包括半颗星评分)实例代码
php485函数参数是什么意思_php485各参数详细说明【介绍】
Windows Hello人脸识别突然无法使用
Laravel如何集成Inertia.js与Vue/React?(安装配置)
哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?
Laravel如何使用Passport实现OAuth2?(完整配置步骤)
JavaScript常见的五种数组去重的方式
Android滚轮选择时间控件使用详解
Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】
猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?
如何在阿里云完成域名注册与建站?
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
Laravel Docker环境搭建教程_Laravel Sail使用指南
详解Android图表 MPAndroidChart折线图
Laravel如何将应用部署到生产服务器_Laravel生产环境部署流程
如何在阿里云虚拟主机上快速搭建个人网站?
Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】
Python图片处理进阶教程_Pillow滤镜与图像增强
C++时间戳转换成日期时间的步骤和示例代码
如何选择PHP开源工具快速搭建网站?
魔方云NAT建站如何实现端口转发?
如何用PHP快速搭建CMS系统?

