c++如何实现多态性_c++ 虚函数表原理与动态绑定机制【教程】
技术百科
裘德小鎮的故事
发布时间:2026-01-01
浏览: 次 只有通过基类指针或引用调用virtual函数才触发动态绑定;直接用对象名调用为静态绑定;virtual须显式写在基类声明前;函数签名须严格匹配;构造函数不能为虚,析构函数应为virtual;每个含虚函数的类有vtable,对象含vptr指向vtable。
虚函数怎么写才触发动态绑定
只有通过基类指针或引用调用 virtual 函数时,C++ 才会启用运行时多态。直接用对象名调用(如 obj.func())永远走静态绑定,编译器在编译期就确定调用哪个函数。
-
virtual必须显式写在基类函数声明前,派生类重写时可加可不加(但建议加上,提高可读性) - 函数签名(包括参数类型、
const修饰、返回类型协变)必须严格匹配,否则是重载而非重写 - 构造函数不能是虚函数;析构函数应声明为
virtual,否则delete基类指针时不会调用派生类析构函数
虚函数表(vtable)长什么样
每个含虚函数的类在编译后都有一个隐式的静态数组(即 vtable),里面存的是该类所有虚函数的函数指针。每个该类的对象开头都隐含一个指针(vptr),指向其所属类的 vtable。
例如:
class Base {
public:
virtual void f() { cout << "Base::f"; }
virtual void g() { cout << "Base::g"; }
};
class Derived : public Base {
public:
void f() override { cout << "Derived::f"; } // 覆盖 Base::f
void h() { cout << "Derived::h"; } // 非虚函数,不进 vtable
};
此时:Base 对象的 vptr 指向含 &Base::f 和 &Base::g 的表;Derived 对象的 vptr 指向另一张表,其中第一项是 &Derived::f(覆盖后的),第二项是 &Base::g(未被重写,沿用基类实现)。
立即学习“C++免费学习笔记(深入)”;
为什么父类指针能调用子类函数
关键在“间接跳转”:当执行 base_ptr->f() 时,编译器生成的指令是——先通过 base_ptr 找到对象首地址,再读取首地址处的 vptr,再根据函数在虚函数表中的索引(比如第 0 项)查出实际函数地址,最后跳过去执行。
- 这个过程发生在运行时,所以叫“动态绑定”
- 如果基类没声明
virtual,编译器根本不会生成 vtable,也不会插入vptr,自然无法动态分发 - 多重继承下 vtable 更复杂(可能多个 vptr),但单继承场景下结构
清晰、开销极小(一次指针解引用 + 一次查表)
容易被忽略的坑:override 和 final 的实际作用
override 不是语法糖,它让编译器检查你是否真的重写了虚函数——若基类没有对应虚函数,或签名不匹配,会直接报错(如 error: 'f' does not override any member functions)。不用 override 可能静默变成重载,导致多态失效。
final 则阻止后续派生类重写该虚函数,也禁止该类被继承(加在类名后)。它影响的是编译期检查,不影响 vtable 布局本身,但能帮助编译器做优化(比如内联判断)。
示例:
struct A {
virtual void foo() {}
};
struct B : A {
void foo() override final {} // OK
};
struct C : B {
void foo() override {} // 错误:B::foo 是 final
};
虚函数机制本身简单,但真正难的是在大型继承体系中保持 vtable 一致性、避免意外切片、以及理解 static_cast / dynamic_cast 对 vptr 的依赖。这些细节一旦出错,往往表现为调用到错误函数或崩溃,且调试时看不到 vtable 的原始布局。
# 是在
# 的是
# 都有
# 多个
# 重写
# 绑定
# 写在
# Error
# 对象
# c++
# 指针
# 子类
# 构造函数
# delete
# 析构函数
# 继承
# 切片
# 多态
# 虚函数
# 派生类
# 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; ?>
】
相关推荐
- 如何使用Golang进行HTTP服务性能测试_测量
- Windows如何拦截2345弹窗广告_Windo
- 如何使用Golang实现多重错误处理_Golang
- Mac自带的词典App怎么用_Mac添加和使用多语
- Win11怎样安装钉钉客户端_Win11安装钉钉教
- Win11如何添加/删除输入法 Win11切换中英
- Win11怎么关闭专注助手 Win11关闭免打扰模
- Mac怎么查看活动监视器_理解Mac进程和资源占用
- Win11如何设置鼠标灵敏度_Win11鼠标灵敏度
- c++的mutex和lock_guard如何使用
- mac怎么打开终端_MAC终端Terminal使用
- Win11怎么关闭自动调节亮度 Win11禁用内容
- Win10 BitLocker加密教程 Win10
- 为什么本地php环境运行php脚本卡顿_php执行
- 如何在 Go 中正确反序列化多个同级 XML 元素
- Python函数接口文档化_自动化说明【指导】
- Windows怎样拦截WPS弹窗广告_Window
- Win11怎么设置闹钟_Windows 11时钟应
- Windows资源管理器总是卡顿或重启怎么办?(修
- php485返回空数组怎么回事_php485数据接
- Win10怎样安装Excel数据分析工具_Win1
- 如何在Golang中捕获HTTP服务器错误_Gol
- C#怎么使用委托和事件 C# delegate与e
- TestNG的testng.xml配置文件怎么写
- c++怎么使用std::unique实现去重_c+
- c++中explicit(bool)的用法 c++
- Windows服务无法启动错误1067是什么_进程
- Python抽象类与接口设计_规范说明【指导】
- Win10怎样清理C盘Steam游戏缓存_Win1
- 为什么Go需要go mod文件_Go go mod
- 如何开启Windows的远程服务器管理工具(RSA
- php增删改查报错1054怎么办_字段名错误排查修
- 如何在Golang中指定模块版本_使用go.mod
- c++如何获取map中所有的键_C++遍历键值对提
- 静态属性修改会影响所有实例吗_php作用域操作符下
- Python安全爬虫设计_IP代理池与验证码识别策
- VSC怎么配置PHP的Xdebug_远程调试设置步
- Win11输入法选字框不见了怎么办_Win11输入
- php打包exe如何加密代码_防反编译保护方法【技
- Win10如何备份注册表_Win10注册表备份步骤
- Win11文件夹预览图不显示怎么办_Win11缩略
- Win11怎么开启游戏工具栏_Windows11
- Win10怎么关闭自动更新错误重启 Win10策略
- Win11怎么恢复误删照片_Win11数据恢复工具
- Bpmn 2.0的XML文件怎么画流程图
- Win11无法安装软件怎么办_Win11解除应用安
- Win10如何卸载WindowsDefender_
- 如何使用Golang配置安全开发环境_防止敏感信息
- Win10如何卸载预装Edge扩展_Win10卸载
- Windows如何查看和管理已安装的字体?(字体文

清晰、开销极小(一次指针解引用 + 一次查表)
QQ客服