c++中如何使用虚函数实现多态_c++多态性实现原理
技术百科
冰火之心
发布时间:2026-01-01
浏览: 次 虚函数必须在基类中用virtual显式声明,否则不触发动态绑定;纯虚函数使类成为抽象类;析构函数需virtual以防资源泄漏;多态须通过指针或引用,避免对象切片;vtable和vptr实现动态绑定;override和final提升安全性。
虚函数必须在基类中用 virtual 显式声明
不加 virtual 的成员函数即使签名相同,也不会触发动态绑定。编译器只看指针/引用的静态类型,调用的是该类型定义的版本,不是实际对象的类型。
常见错误是忘记在基类声明 virtual,或者误以为“子类重写就自动多态”——C++ 不自动推导,必须显式标记。
-
virtual只需出现在基类声明中,派生类重写时加不加都可(但建议加上,提高可读性) - 纯虚函数写法是
virtual void func() = 0;,含纯虚函数的类即抽象类,不能实例化 - 析构函数若可能通过基类指针删除派生类对象,必须声明为
virtual,否则派生类部分不会被析构
多态调用必须通过指针或引用,不能直接用对象
值传递会触发对象切片(slicing),丢失派生类特有成员和虚函数表信息,调用的永远是基类版本。
Base b = Derived(); // 切片!b 只剩 Base 部分 b.func(); // 静态绑定,调用 Base::func() Base* ptr = new Derived(); ptr->func(); // 动态绑定,调用 Derived::func()
- 只有
Base*或Base&才能触发动态绑定 -
Base对象本身、Base类型的局部变量、函数按值传参,全部走静态绑定 - 返回值也不能是基类对象(同样切片),应返回指针或智能指针
虚函数表(vtable)和虚指针(vptr)是实现核心
每个含虚函数的类编译时生成一张虚函数表(vtable),存放该类所有虚函数的地址;每个对象实例开头隐式插入一个虚指针(vptr),指向其所属类的 vtable。
运行时,通过 ptr->func() 调用时,编译器生成的代码实际是:(*ptr->vptr[索引])(ptr) —— 先取 vptr,再查表,最后调用。
- vtable 是类级别的,所有该类对象共享同一张表
- vptr 是对象级别的,在构造函数中由编译器自动初始化(先父后子,所以构造期间虚函数调用仍走当前类的 vtable)
- 虚函数调用比
普通函数多一次内存寻址(vptr → vtable → 函数地址),有轻微开销,但现代 CPU 分支预测能缓解
override 和 final 关键字能避免常见误写
不加 override 时,拼错函数名、参数类型不匹配、const 修饰不一致,都会导致“看似重写实则新增”,失去多态效果且无编译错误。
class Base { virtual void foo(int) const; };
class Derived : public Base {
void foo(int) {} // 缺 const → 新函数,非重写!
void foo(int) const override; // 正确:编译器检查是否真能重写
void bar() const override; // 错误:Base 没有 bar → 编译失败
};-
override强制编译器验证:该函数是否确实重写了基类虚函数 -
final加在函数后(virtual void f() final;)禁止进一步重写;加在类后(class D final : B {})禁止继承 - 这两个关键字不改变语义,但极大提升接口意图清晰度和错误捕获能力
虚函数多态看似简单,真正容易出问题的地方在于:对象生命周期管理(尤其是 virtual 析构)、切片场景的误用、以及未加 override 导致的静默失效。这些都不是语法错误,而是逻辑陷阱,调试时很难一眼发现。
# 的是
# 重写
# 绑定
# 对象
# c++
# void
# class
# 指针
# 子类
# 构造函数
# 接口
# 成员函数
# 析构函数
# 类中
# 继承
# 切片
# 多态
# 虚函数
# 派生类
# const
# 局部变量
# 值传递
# 编译错误
# 不加
# 或引用
# 纯虚函数
# 加在
# 抽象类
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- VSC怎样在Linux运行PHP_Ubuntu系统
- Win11怎么设置虚拟内存_Windows 11优
- Windows蓝屏错误0x00000023怎么修复
- Python与OpenAI接口集成实战_生成式AI
- Windows10系统怎么查看IP地址_Win10
- Win11怎么关闭小组件_Win11禁用任务栏天气
- LINUX如何开放防火墙端口_Linux fire
- mac怎么退出id_MAC退出iCloud账号与A
- XSLT怎么生成动态的HTML属性名和标签名
- VSC怎么快速定位PHP错误行_错误追踪设置法【方
- 如何使用Golang reflect检查方法数量_
- Win10系统怎么查看显卡温度_Win10任务管理
- Django 测试数据库表缺失与字段未创建问题的完
- windows 10专注助手怎么关闭_window
- Windows如何使用注册表查找和删除项?(reg
- Win11怎么关闭系统推荐内容_Windows11
- Python与GPU加速技术_CUDA与Numba
- Win11触摸板没反应怎么办_开启Win11笔记本
- Win11怎么连接投影仪_Win11多显示器投屏设
- PHP主流架构怎么部署到Docker_容器化流程【
- Win11怎么查看激活状态_查询Windows 1
- Win11怎么看电池循环次数_Win11笔记本电池
- c++中的Tag Dispatching是什么_c
- Flask 表单数据通过 SMTP 发送邮件的完整
- 如何用::实现工具类方法调用_php静态工具类设计
- Windows蓝屏BAD_POOL_HEADER故
- php订单日志权限怎么设_php订单日志文件权限设
- c# Task.ConfigureAwait(tr
- php转mp4怎么设置帧率_调整php生成mp4视
- 如何在Golang中实现WebSocket广播_使
- Mac自带的词典App怎么用_Mac添加和使用多语
- Python技术债务管理_长期维护解析【教程】
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- 如何开启Windows的远程服务器管理工具(RSA
- c++如何实现多态性_c++ 虚函数表原理与动态绑
- 如何在Golang中处理云原生事件_使用Event
- php485读数据时阻塞怎么办_php485非阻塞
- 如何解决同一段404代码在不同主机上表现不一致的问
- 如何快速验证Golang安装是否成功_运行go v
- Win11怎么把图标拖到任务栏_Win11固定应用
- 如何使用Golang实现跨域请求支持_Golang
- Win10怎样设置多显示器_Win10多显示器扩展
- Win11怎么关闭触摸键盘图标_Windows11
- Go语言中CookieJar的持久化机制解析:内存
- php中::能用于接口静态方法吗_接口静态方法调用
- 如何在Golang中处理通道发送接收错误_防止阻塞
- 如何使用Golang实现路由参数绑定_使用Mux和
- Windows 10怎么录屏_Windows 10
- 如何使用正则表达式批量替换重复的“-”模式为固定字
- php打包exe后无法写入文件_权限问题解决方法【

普通函数多一次内存寻址(vptr → vtable → 函数地址),有轻微开销,但现代 CPU 分支预测能缓解
QQ客服