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

C++笔记(面向对象)静态联编和动态联编

1. 基本概念

联编(Binding) 指的是将函数调用与函数实现关联起来的过程。

静态联编(Static Binding)

  • 也称为早期绑定(Early Binding)

  • 编译期间确定调用哪个函数

  • 基于变量的静态类型(声明类型)

  • C++语言中,使用对象名加点“.”成员选择运算符,去调用对象虚函数,则被调用的虚函数是在编译和链接时确定。(称为静态联编)。

动态联编(Dynamic Binding)

  • 也称为晚期绑定(Late Binding)

  • 运行期间确定调用哪个函数

  • 基于对象的实际类型(动态类型)

C++语言中,使用类类型的引用或指针调用虚函数(成员选择符“->”),则程序在运行时选择虚函
数的过程,称为动态联编


2. 静态联编详解

2.1 静态联编的几种情况

cpp

#include <iostream>
using namespace std;class Base {
public:void nonVirtualFunc() {cout << "Base::nonVirtualFunc" << endl;}void overloadedFunc(int x) {cout << "Base::overloadedFunc(int): " << x << endl;}void overloadedFunc(double x) {cout << "Base::overloadedFunc(double): " << x << endl;}
};class Derived : public Base {
public:// 隐藏基类的nonVirtualFunc,不是重写!void nonVirtualFunc() {cout << "Derived::nonVirtualFunc" << endl;}// 添加新的重载版本void overloadedFunc(const string& s) {cout << "Derived::overloadedFunc(string): " << s << endl;}
};void testStaticBinding() {cout << "=== 静态联编演示 ===" << endl;Derived derived;Base* basePtr = &derived;  // 基类指针指向派生类对象// 情况1:非虚函数调用 - 静态联编basePtr->nonVirtualFunc();  // 输出: Base::nonVirtualFuncderived.nonVirtualFunc();   // 输出: Derived::nonVirtualFunc// 情况2:函数重载 - 静态联编derived.overloadedFunc(10);     // 输出: Base::overloadedFunc(int): 10derived.overloadedFunc(3.14);   // 输出: Base::overloadedFunc(double): 3.14derived.overloadedFunc("hello");// 输出: Derived::overloadedFunc(string): hello// 情况3:通过基类指针只能看到基类的重载basePtr->overloadedFunc(20);    // 输出: Base::overloadedFunc(int): 20basePtr->overloadedFunc(2.71);  // 输出: Base::overloadedFunc(double): 2.71// basePtr->overloadedFunc("world");  // ❌ 错误:基类没有string版本
}

2.2 静态联编的原理

编译器的处理过程:

cpp

// 源代码
Base* ptr = new Derived();
ptr->nonVirtualFunc();// 编译器的处理(概念上):
// 1. 查看ptr的声明类型:Base*
// 2. 在Base类中查找nonVirtualFunc
// 3. 生成调用Base::nonVirtualFunc的代码
// 4. 函数地址在编译时就已经确定

汇编级别看静态联编:

assembly

; ptr->nonVirtualFunc() 的编译结果
call Base::nonVirtualFunc  ; 直接调用,地址在编译时确定

3. 动态联编详解

3.1 动态联编的实现:虚函数

cpp

class Base {
public:// 虚函数 - 支持动态联编virtual void virtualFunc() {cout << "Base::virtualFunc" << endl;}virtual void anotherVirtual() {cout << "Base::anotherVirtual" << endl;}// 虚析构函数 - 重要!virtual ~Base() {cout << "Base destructor" << endl;}
};class Derived : public Base {
public:// 重写虚函数void virtualFunc() override {cout << "Derived::virtualFunc" << endl;}void anotherVirtual() override {cout << "Derived::anotherVirtual" << endl;}~Derived() override {cout << "Derived destructor" << endl;}
};class SecondDerived : public Derived {
public:void virtualFunc() override {cout << "SecondDerived::virtualFunc" << endl;}
};void testDynamicBinding() {cout << "\n=== 动态联编演示 ===" << endl;Base* basePtr1 = new Derived();Base* basePtr2 = new SecondDerived();Derived* derivedPtr = new SecondDerived();// 动态联编:根据实际对象类型调用函数basePtr1->virtualFunc();     // 输出: Derived::virtualFuncbasePtr2->virtualFunc();     // 输出: SecondDerived::virtualFuncderivedPtr->virtualFunc();   // 输出: SecondDerived::virtualFunc// 虚析构函数也是动态联编delete basePtr1;  // 先调用Derived::~Derived,再调用Base::~Basedelete basePtr2;  // 先调用SecondDerived::~SecondDerived,再调用Derived::~Derived,最后Base::~Basedelete derivedPtr;
}

3.2 动态联编的原理

编译器的处理过程:

cpp

// 源代码
Base* ptr = new Derived();
ptr->virtualFunc();// 编译器的处理(概念上):
// 1. 发现virtualFunc是虚函数
// 2. 生成通过vtable间接调用的代码:
//    - 通过ptr找到vptr
//    - 通过vptr找到vtable
//    - 在vtable中找到virtualFunc的地址
//    - 调用该地址对应的函数

汇编级别看动态联编:

assembly

; ptr->virtualFunc() 的编译结果
mov rax, qword ptr [ptr]      ; 获取对象地址
mov rax, qword ptr [rax]      ; 获取vptr(对象的前8字节)
mov rax, qword ptr [rax]      ; 获取vtable中第一个函数的地址
call rax                      ; 间接调用,地址在运行时确定

4. 对比实验:区分两种联编

cpp

class Calculator {
public:// 静态联编:函数重载int calculate(int a, int b) {cout << "Calculator::calculate(int, int)" << endl;return a + b;}double calculate(double a, double b) {cout << "Calculator::calculate(double, double)" << endl;return a * b;}// 动态联编:虚函数virtual double compute(int a, int b) {cout << "Calculator::compute" << endl;return a * b;}
};class ScientificCalculator : public Calculator {
public:// 隐藏基类的calculate(静态联编)double calculate(double a, double b) {cout << "ScientificCalculator::calculate(double, double)" << endl;return a / b;}// 重写虚函数(动态联编)double compute(int a, int b) override {cout << "ScientificCalculator::compute" << endl;return pow(a, b);}
};void contrastExperiment() {cout << "=== 联编方式对比实验 ===" << endl;ScientificCalculator sciCalc;Calculator* calcPtr = &sciCalc;cout << "\n1. 静态联编(非虚函数):" << endl;sciCalc.calculate(10, 20);       // 调用基类版本(静态联编)sciCalc.calculate(10.0, 20.0);   // 调用派生类版本(静态联编)calcPtr->calculate(5, 3);        // 调用基类版本(静态联编)cout << "\n2. 动态联编(虚函数):" << endl;sciCalc.compute(2, 3);           // 调用派生类版本calcPtr->compute(2, 3);          // 调用派生类版本(动态联编!)
}

5. 联编方式的决定因素

5.1 影响联编方式的因素

cpp

class Test {
public:void normalMethod() {}           // 静态联编virtual void virtualMethod() {}  // 动态联编
};void analyzeBindingFactors() {Test obj;Test* ptr = &obj;Test& ref = obj;cout << "=== 联编方式分析 ===" << endl;// 情况1:通过对象调用 - 总是静态联编obj.normalMethod();    // 静态联编obj.virtualMethod();   // 静态联编!(编译器可以确定对象类型)// 情况2:通过指针调用 - 根据函数类型决定ptr->normalMethod();   // 静态联编ptr->virtualMethod();  // 动态联编// 情况3:通过引用调用 - 根据函数类型决定ref.normalMethod();    // 静态联编ref.virtualMethod();   // 动态联编// 情况4:在构造函数中调用// 情况5:在析构函数中调用// (构造函数和析构函数中总是静态联编)
}

5.2 构造函数和析构函数中的联编

cpp

class Base {
public:Base() {cout << "Base构造函数中: ";show();  // 静态联编!在构造期间对象还不是派生类}virtual ~Base() {cout << "Base析构函数中: ";show();  // 静态联编!在析构期间对象已不是派生类}virtual void show() {cout << "Base::show" << endl;}
};class Derived : public Base {
public:Derived() {cout << "Derived构造函数中: ";show();  // 静态联编}~Derived() override {cout << "Derived析构函数中: ";show();  // 静态联编}void show() override {cout << "Derived::show" << endl;}
};void testConstructorBinding() {cout << "\n=== 构造/析构函数中的联编 ===" << endl;Derived derived;cout << "--- 对象创建完成 ---" << endl;// 正常使用时的动态联编Base* ptr = &derived;cout << "正常使用: ";ptr->show();  // 动态联编:Derived::show
}

输出结果:

text

Base构造函数中: Base::show
Derived构造函数中: Derived::show
--- 对象创建完成 ---
正常使用: Derived::show
Derived析构函数中: Derived::show
Base析构函数中: Base::show

6. 性能对比分析

6.1 性能测试代码

cpp

#include <chrono>
using namespace std::chrono;class PerformanceTest {
public:void staticMethod() {// 一些简单工作volatile int result = 0;for (int i = 0; i < 100; ++i) {result += i;}}virtual void virtualMethod() {// 同样的工作volatile int result = 0;for (int i = 0; i < 100; ++i) {result += i;}}
};void performanceComparison() {const int iterations = 100000000; // 1亿次调用PerformanceTest obj;PerformanceTest* ptr = &obj;cout << "=== 性能对比 ===" << endl;// 测试静态联编性能auto start1 = high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {obj.staticMethod();  // 静态联编}auto end1 = high_resolution_clock::now();// 测试动态联编性能auto start2 = high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {ptr->virtualMethod();  // 动态联编}auto end2 = high_resolution_clock::now();auto static_time = duration_cast<milliseconds>(end1 - start1);auto virtual_time = duration_cast<milliseconds>(end2 - start2);cout << "静态联编: " << static_time.count() << "ms" << endl;cout << "动态联编: " << virtual_time.count() << "ms" << endl;cout << "性能差异: " << (virtual_time.count() - static_time.count()) << "ms" << endl;
}

7. 实际应用建议

7.1 选择联编方式的准则

cpp

// 适合静态联编的情况:
class MathUtils {
public:// 工具函数,行为固定static double sqrt(double x) { return std::sqrt(x); }static double sin(double x) { return std::sin(x); }// 重载函数,编译时就能确定int process(int x) { return x * 2; }double process(double x) { return x * 1.5; }
};// 适合动态联编的情况:
class Document {
public:virtual void save() = 0;      // 不同文档格式保存方式不同virtual void print() = 0;     // 打印行为可能不同virtual void close() = 0;     // 关闭前的清理工作不同virtual ~Document() = default;
};class PdfDocument : public Document {void save() override { /* PDF保存逻辑 */ }void print() override { /* PDF打印逻辑 */ }void close() override { /* PDF关闭逻辑 */ }
};class WordDocument : public Document {void save() override { /* Word保存逻辑 */ }void print() override { /* Word打印逻辑 */ }void close() override { /* Word关闭逻辑 */ }
};

7.2 设计原则

  1. 默认使用静态联编:性能更好

  2. 需要多态时使用动态联编:当行为需要根据实际对象类型变化时

  3. 基类析构函数应该是虚函数:确保正确清理资源

  4. 避免在构造/析构函数中调用虚函数:此时不是多态行为


8. 总结

静态联编 vs 动态联编

特性静态联编动态联编
确定时间编译期间运行期间
决定依据变量/参数的静态类型对象的实际类型
实现机制直接函数调用通过vtable间接调用
性能高效有额外开销
灵活性较低很高
适用场景函数重载、模板、非虚函数虚函数、多态

关键要点:

  1. 静态联编是默认行为,适用于大多数情况

  2. 动态联编通过虚函数实现,支持运行时多态

  3. 联编方式在调用时确定,不是声明时

  4. 理解两种联编的区别有助于写出高效、正确的代码

http://www.dtcms.com/a/558328.html

相关文章:

  • 【递归、回溯、搜索】专题六:记忆化搜索
  • 网站页面描述怎么写咸阳鑫承网站建设
  • 如何在百度举报网站桂林市临桂区
  • 一个可以做行程的网站网站虚拟主机1g
  • 网站标题和关键词一样网页版微信登录不了怎么解决
  • 2015年做啥网站能致富wordpress 导出export.php
  • 开网站做商城怎么样北京ui网页设计培训
  • WindoWs 系统管理批处理脚本
  • 【大模型训练】zero1与zero
  • 网站特效怎么做品牌设计公司vi设计
  • 图片墙网站源码网站建设售后服务方案
  • 《算法通关指南:数据结构和算法篇 --- 顺序表相关算法题》--- 1.移动零,2.颜色分类
  • 呼和浩特市做网站公司好的电子工程网络信息技术专业
  • php网站开发账号密码西安十大广告设计公司
  • 南山做网站的公司网站改版是否有影响
  • 叙述网站的建设意义所在建设互联网站
  • wordpress能发多少邮件东莞关键词优化排名
  • Apache Spark算法开发指导-特征转换RobustScaler
  • 广东省省考备考(第一百三十九天11.1)——判断推理、资料分析、数量关系(强化训练)
  • 自己建网站有什么用网站运营需要哪些知识
  • 网站 app建设开发合作协议有没有做推文的网站
  • 企业做网站公司排名口碑硬件开发平台
  • 德州做网站的公司南昌网站建设哪家就好
  • 【python】装饰器
  • 培训计划--linux基础操作
  • 做一网站要什么时候开始企业网站的建设包括哪些
  • 023数据结构之线段树——算法备赛
  • 做化工回收的 做那个网站广东新闻发布会
  • 《信息系统项目管理师》2024 年上第 2 批次案例分析题及解析
  • 华为OD机试双机位A卷 - 插队 (C++ Python JAVA JS GO)