当前位置: 首页 > news >正文

静态联编与动态联编

静态联编与动态联编

一、联编的基本概念

联编是指计算机程序将标识符名存储地址关联起来的过程,也就是将函数调用与函数入口地址相结合的过程**。

联编的本质(函数调用与函数入口地址相结合) 函数调用:func(10, 20) 联编前: call func (未知地址) ↓ 联编过程 联编后: call 0x00401234 (具体内存地址)

二、联编的分类

类型别名绑定时机实现方式效率
静态联编早期绑定编译/链接阶段直接函数调用
动态联编晚期绑定、滞后联编运行阶段通过虚表间接调用略低

三、静态联编(早期绑定)

3.1 静态联编的定义

静态联编是指在编译和链接阶段,就将函数实现和函数调用关联起来。

C语言中的所有联编都是静态联编,C++中也支持静态联编。

静态联编的处理流程 源代码 编译阶段 func(10, 20); --> 编译器查找 func 的函数地址 ↓ 生成直接调用指令 call 0x00401234 特点: 地址在编译时就已确定 执行时直接跳转到该地址 没有任何运行时开销

3.2 触发静态联编的情况

3.2.1 通过对象调用虚函数(使用.运算符)
classObject{public:virtualvoidadd(intx){cout<<"Object::add(int)"<<endl;}};intmain(){Object obj;obj.add(10);// ! 通过对象调用虚函数 → 静态联编return0;}

原因通过对象名调用时,对象的类型在编译时完全确定,编译器可以直接确定调用哪个函数。

3.2.2 普通函数调用
voidfunc(intx){cout<<x<<endl;}intmain(){func(10);// 静态联编:地址在编译时确定return0;}
3.2.3 非虚成员函数调用
classObject{public:voidfunc(){cout<<"Object::func"<<endl;}// 非虚函数};intmain(){Object obj;obj.func();// 静态联编return0;}

四、动态联编(晚期绑定)

4.1 动态联编的定义

动态联编是指在程序执行时才将函数实现和函数调用关联起来。

C++中通过基类指针或引用调用虚函数时,会在运行时根据实际对象类型选择调用哪个函数

动态联编的处理流程 源代码 编译阶段 p->add(10); ----> 生成间接调用代码 mov eax, [p] mov ecx, [eax] call [ecx + offset] ↓ 运行阶段:根据 p 实际指向的对象类型动态决定 p 指向 Object → 调用 Object::add() p 指向 Base → 调用 Base::add()

4.2 触发动态联编的条件

条件说明
1. 公有继承派生类 is-a 基类
2. 虚函数基类函数声明为 virtual
3. 派生类重写派生类提供自己的实现
4. 基类指针/引用通过指针(->)或引用(.)调用
classObject{public:virtualvoidadd(intx){cout<<"Object::add(int)"<<endl;}};classBase:publicObject{public:virtualvoidadd(intx)override{cout<<"Base::add(int)"<<endl;}};intmain(){Base base;Object*p=&base;p->add(10);// 动态联编:调用 Base::add()Object&r=base;r.add(10);// 动态联编:调用 Base::add()return0;}

五、构造与析构中的联编

5.1 构造函数中的联编

在构造函数中调用虚函数时,采用静态联编(不会调用派生类的重写版本)。

classObject{public:Object(intx=0):value(x){cout<<"Create Object: "<<endl;add(12);// !调用 Object::add(),不是派生类的版本}virtualvoidadd(intx){cout<<"Object::add(int)"<<endl;}private:intvalue;};classBase:publicObject{public:Base(intx=0):Object(x+10),num(x){cout<<"Create Base"<<endl;add(1);// 此时虚表已指向 Base,调用 Base::add()}virtualvoidadd(inta)override{cout<<"Base::add(int)"<<endl;}private:intnum;};intmain(){Base base;return0;}

输出

Create Object: Object::add(int) ← 构造函数中调用的是 Object 版本 Create Base Base::add(int) ← 此时虚表已设置,调用 Base 版本

原因图解

构造过程中的虚表指针变化 Base base; 构造过程: 阶段1:Object 构造函数执行 base._vfptr → Object 虚表 此时 add() 调用 Object::add() ↓ 阶段2:Base 构造函数执行 base._vfptr → Base 虚表 此时 add() 调用 Base::add() 结论:构造过程中,虚表指针逐级更新 当前构造到哪一层,虚表就指向哪一层

5.2 析构函数中的联编

在析构函数中调用虚函数时,也采用静态联编

classObject{public:virtual~Object(){cout<<"Destroy Object"<<endl;add(23);// !调用 Object::add(),不是派生类的版本}virtualvoidadd(intx){cout<<"Object::add(int)"<<endl;}};classBase:publicObject{public:~Base()override{cout<<"Destroy Base"<<endl;add(2);// 此时调用 Base::add()}voidadd(inta)override{cout<<"Base::add(int)"<<endl;}};intmain(){Base base;return0;}

输出

Destroy Base Base::add(int) ← 先调用派生类析构,此时虚表仍指向 Base Destroy Object Object::add(int) ← 基类析构时,虚表已恢复为 Object

析构过程中的虚表指针变化

析构过程中的虚表指针变化 Base base; 析构过程: 阶段1:Base 析构函数执行 base._vfptr → Base 虚表 此时 add() 调用 Base::add() ↓ 阶段2:Object 析构函数执行 ↓ base._vfptr → Object 虚表(恢复) 此时 add() 调用 Object::add()

六、静态联编 与 动态联编对比

6.1 核心区别对比表

对比维度静态联编动态联编
绑定时机编译/链接阶段运行阶段
函数地址确定编译时确定运行时确定
调用指令直接调用call address间接调用call [vptr+offset]
性能开销一次间接寻址
灵活性
适用场景类型确定的情况需要多态的情况

6.2 调用方式对比

调用方式联编类型示例
对象 +.调用虚函数静态联编obj.add(10)
对象 +.调用普通函数静态联编obj.func()
指针 +->调用虚函数动态联编p->add(10)
引用 +.调用虚函数动态联编r.add(10)
指针 +->调用普通函数静态联编p->func()

七、完整示例代码

#include<iostream>usingnamespacestd;classObject{private:intvalue;public:Object(intx=0):value(x){cout<<"Object 构造"<<endl;add(12);// 静态联编:调用 Object::add()}virtual~Object(){cout<<"Object 析构"<<endl;add(23);// 静态联编:调用 Object::add()}virtualvoidadd(intx){cout<<"Object::add("<<x<<")"<<endl;}voidnormalFunc(){cout<<"Object::normalFunc"<<endl;}};classBase:publicObject{private:intnum;public:Base(intx=0):Object(x+10),num(x){cout<<"Base 构造"<<endl;add(1);// 此时虚表已指向 Base,调用 Base::add()}~Base()override{cout<<"Base 析构"<<endl;add(2);// 调用 Base::add()}voidadd(inta)override{cout<<"Base::add("<<a<<")"<<endl;}voidnormalFunc(){cout<<"Base::normalFunc"<<endl;}};intmain(){cout<<"=== 对象调用(静态联编) ==="<<endl;Base base;base.add(100);// 静态联编:通过对象调用base.normalFunc();// 静态联编:普通函数cout<<"\n=== 指针调用(动态联编) ==="<<endl;Object*p=&base;p->add(200);// 动态联编:通过指针调用虚函数p->normalFunc();// 静态联编:普通函数cout<<"\n=== 引用调用(动态联编) ==="<<endl;Object&r=base;r.add(300);// 动态联编:通过引用调用虚函数r.normalFunc();// 静态联编:普通函数return0;}

输出

=== 对象调用(静态联编) === Object 构造 Object::add(12) ← 构造时静态联编 Base 构造 Base::add(1) ← 构造时动态联编(虚表已设置) Base::add(100) ← 通过对象调用(静态) Base::normalFunc === 指针调用(动态联编) === Base::add(200) ← 通过指针调用虚函数(动态) Object::normalFunc ← 普通函数(静态) === 引用调用(动态联编) === Base::add(300) ← 通过引用调用虚函数(动态) Object::normalFunc ← 普通函数(静态) Base 析构 Base::add(2) ← 析构时调用 Object 析构 Object::add(23) ← 析构时静态联编

八、总结

场景联编类型原因
C 语言所有函数调用静态联编无继承和多态
对象名调用虚函数静态联编编译时类型确定
对象名调用普通函数静态联编非虚函数
指针调用虚函数动态联编运行时类型可能变化
引用调用虚函数动态联编运行时类型可能变化
指针调用普通函数静态联编非虚函数
构造函数中调用虚函数静态联编派生类未构造完成
析构函数中调用虚函数静态联编派生类已开始析构
http://www.jsqmd.com/news/1181424/

相关文章:

  • 福州上门回收黄金须知!2026 年7月行情,正规商家交易注意事项 - 小蝶回收测评
  • Windows系统文件comuid.dll丢失找不到问题解决
  • GitHub上那些值得关注的RAG开源项目:从文档解析到多模态检索
  • 为什么你的AI Agent正在悄悄上传用户对话?——从TensorFlow Serving到LangChain SDK的6个默认风险配置
  • 热门公告:卡地亚复杂功能腕表维修标准指南 2026 线下服务地址汇总 - 卡地亚售后服务中心
  • 5分钟快速解锁Wand游戏修改器:免费增强方案终极指南
  • GPT-5.6、Claude、Gemini、DeepSeek 怎么选?按任务选型的终极对比清单
  • Codex 接入团队:Demo 跑得通,上线却崩盘?权限与日志才是生死线
  • 如何免费解锁Wand专业版:简单三步实现无限游戏体验终极指南
  • 很多人分不清火线、零线、地线,看完终于明白了
  • AMD Ryzen AI混合优化:Mistral-7B-Instruct-v0.3_rai_1.7.1_npu_4K的token后端配置
  • 集成学习之随机森林实战:从Bagging到分类预测
  • 2026年金华义乌电商侵权维权如何不踩雷?5家实测对比与源头快速处置指南 - 中国品牌企业观察网
  • Mask R-CNN 气球分割实战:从 VIA 标注到 PyTorch 训练,Loss 降至 0.02
  • 新手必看:AnyFlow-FAR-Wan2.1-14B-Diffusers常见问题与解决方案汇总
  • OpenDesign Internship项目全面解析:从基础架构到实践应用的完整指南
  • 高中生手机怎么选靠谱品牌?2026年5大核心维度横向对比分析 - 科技焦点
  • 泰戈尔的诗歌15
  • Go语言中的Gotalk高级用法:并发、超时与资源管理终极指南
  • 为什么选择NVIDIA ESM-2?对比原版ESM-2的10大优化特性
  • Chatterbox TTS架构设计实战:企业级语音合成解决方案
  • 全球域名 WHOIS 信息实时查询免费 API 接口教程,支持1000+后缀
  • 生命涌现的小龙虾技能之【Flowering Date Prediction | 开花植物花期预测】简介
  • 为什么选择gala-filetrace?openEuler系统配置审计的7大优势
  • 深入理解Attention Sinks的KV缓存机制:代码级实现分析
  • 2026年恒温振荡混匀仪厂家实力排行实测对比:北京科誉兴业以数据驱动精密混匀方案引领行业 - 品牌推荐大师1
  • WandEnhancer终极指南:一键解锁WeMod高级功能的完整解决方案
  • 多态与虚函数(理论)
  • wine-app配置脚本详解:10个实用技巧优化应用兼容性
  • 为什么选择Llama-3.1-8B-Instruct-MXFP4-W4A4:4位量化对比8位量化的核心优势