
静态联编与动态联编一、联编的基本概念联编是指计算机程序将标识符名与存储地址关联起来的过程也就是将函数调用与函数入口地址相结合的过程**。联编的本质函数调用与函数入口地址相结合 函数调用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){coutObject::add(int)endl;}};intmain(){Object obj;obj.add(10);// 通过对象调用虚函数 → 静态联编return0;}原因通过对象名调用时对象的类型在编译时完全确定编译器可以直接确定调用哪个函数。3.2.2 普通函数调用voidfunc(intx){coutxendl;}intmain(){func(10);// 静态联编地址在编译时确定return0;}3.2.3 非虚成员函数调用classObject{public:voidfunc(){coutObject::funcendl;}// 非虚函数};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. 虚函数基类函数声明为 virtual3. 派生类重写派生类提供自己的实现4. 基类指针/引用通过指针-或引用.调用classObject{public:virtualvoidadd(intx){coutObject::add(int)endl;}};classBase:publicObject{public:virtualvoidadd(intx)override{coutBase::add(int)endl;}};intmain(){Base base;Object*pbase;p-add(10);// 动态联编调用 Base::add()Objectrbase;r.add(10);// 动态联编调用 Base::add()return0;}五、构造与析构中的联编5.1 构造函数中的联编在构造函数中调用虚函数时采用静态联编不会调用派生类的重写版本。classObject{public:Object(intx0):value(x){coutCreate Object: endl;add(12);// 调用 Object::add()不是派生类的版本}virtualvoidadd(intx){coutObject::add(int)endl;}private:intvalue;};classBase:publicObject{public:Base(intx0):Object(x10),num(x){coutCreate Baseendl;add(1);// 此时虚表已指向 Base调用 Base::add()}virtualvoidadd(inta)override{coutBase::add(int)endl;}private:intnum;};intmain(){Base base;return0;}输出Create Object: Object::add(int) ← 构造函数中调用的是 Object 版本 Create Base Base::add(int) ← 此时虚表已设置调用 Base 版本原因图解构造过程中的虚表指针变化 Base base; 构造过程 阶段1Object 构造函数执行 base._vfptr → Object 虚表 此时 add() 调用 Object::add() ↓ 阶段2Base 构造函数执行 base._vfptr → Base 虚表 此时 add() 调用 Base::add() 结论构造过程中虚表指针逐级更新 当前构造到哪一层虚表就指向哪一层5.2 析构函数中的联编在析构函数中调用虚函数时也采用静态联编。classObject{public:virtual~Object(){coutDestroy Objectendl;add(23);// 调用 Object::add()不是派生类的版本}virtualvoidadd(intx){coutObject::add(int)endl;}};classBase:publicObject{public:~Base()override{coutDestroy Baseendl;add(2);// 此时调用 Base::add()}voidadd(inta)override{coutBase::add(int)endl;}};intmain(){Base base;return0;}输出Destroy Base Base::add(int) ← 先调用派生类析构此时虚表仍指向 Base Destroy Object Object::add(int) ← 基类析构时虚表已恢复为 Object析构过程中的虚表指针变化析构过程中的虚表指针变化 Base base; 析构过程 阶段1Base 析构函数执行 base._vfptr → Base 虚表 此时 add() 调用 Base::add() ↓ 阶段2Object 析构函数执行 ↓ base._vfptr → Object 虚表恢复 此时 add() 调用 Object::add()六、静态联编 与 动态联编对比6.1 核心区别对比表对比维度静态联编动态联编绑定时机编译/链接阶段运行阶段函数地址确定编译时确定运行时确定调用指令直接调用call address间接调用call [vptroffset]性能开销无一次间接寻址灵活性低高适用场景类型确定的情况需要多态的情况6.2 调用方式对比调用方式联编类型示例对象 .调用虚函数静态联编obj.add(10)对象 .调用普通函数静态联编obj.func()指针 -调用虚函数动态联编p-add(10)引用 .调用虚函数动态联编r.add(10)指针 -调用普通函数静态联编p-func()七、完整示例代码#includeiostreamusingnamespacestd;classObject{private:intvalue;public:Object(intx0):value(x){coutObject 构造endl;add(12);// 静态联编调用 Object::add()}virtual~Object(){coutObject 析构endl;add(23);// 静态联编调用 Object::add()}virtualvoidadd(intx){coutObject::add(x)endl;}voidnormalFunc(){coutObject::normalFuncendl;}};classBase:publicObject{private:intnum;public:Base(intx0):Object(x10),num(x){coutBase 构造endl;add(1);// 此时虚表已指向 Base调用 Base::add()}~Base()override{coutBase 析构endl;add(2);// 调用 Base::add()}voidadd(inta)override{coutBase::add(a)endl;}voidnormalFunc(){coutBase::normalFuncendl;}};intmain(){cout 对象调用静态联编 endl;Base base;base.add(100);// 静态联编通过对象调用base.normalFunc();// 静态联编普通函数cout\n 指针调用动态联编 endl;Object*pbase;p-add(200);// 动态联编通过指针调用虚函数p-normalFunc();// 静态联编普通函数cout\n 引用调用动态联编 endl;Objectrbase;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 语言所有函数调用静态联编无继承和多态对象名调用虚函数静态联编编译时类型确定对象名调用普通函数静态联编非虚函数指针调用虚函数动态联编运行时类型可能变化引用调用虚函数动态联编运行时类型可能变化指针调用普通函数静态联编非虚函数构造函数中调用虚函数静态联编派生类未构造完成析构函数中调用虚函数静态联编派生类已开始析构