C++ — 静态绑定与动态绑定

本文通过具体的C++代码示例,详细解释了静态绑定和动态绑定的区别,特别是virtual函数与缺省参数值的不同绑定方式及其原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 静态绑定与动态绑定

——————————————————————————————————



静态绑定,动态绑定算是一个比较偏的知识点,这个也是我在Effective C++这本书当中学习到的. 我觉得很有必要写一篇博客记.

首先我们要知道静态类型和动态类型的概念:

静态类型:就是它在程序中被声明时所采用的类型(或理解为类型指针或引用的字面类型),在编译期确定;

动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。

接下来我引用Effective C++这本书中的例子,帮助理解:

class Shape
{
public:
	enum shapeColor{Red,Green,Blue};
	//所有形状都必须提供一个函数,同来绘出自己.
	virtual void draw(shapeColor color = Red) const = 0;
	...
};
class Rectangle : public Shape
{
public:
	//注意,赋予不同的缺省参数值,这真糟糕
	virtual void draw(shapeColor color = Green) const;
};
class Circle : public Shape
{
public:

	virtual void draw(shapeColor color) const;
	//请注意,以上这么写则当客户以对象调用此函数,一定要指定参数值.
	//因为静态绑定下这个函数并不从其base继承缺省参数值
	//但若以指针(或reference)调用此函数,可以不指定参数值
	//因为动态绑定下这个函数会从其base继承缺省参数值
	......

};
Shape *ps;         // ps的静态类型为Shape*,它没有动态类型,因为它尚未指向任何对象;  
Shape *pc = new Circle;      // pc的静态类型为Shape*,它的动态类型为Circle*;  
Shape *pr = new Rectangle;   // pr的静态类型为Shape*,它的动态类型为Rectangle*;  



// 动态类型可以在程序执行过程中改变(通常是经由赋值动作):  
ps = pc;                     // ps的动态类型如今是Circle*;  
ps = pr;                     // ps的动态类型如今是Rectangle*;  



现在我们来理解何为静态绑定和动态绑定???

静态绑定:又名前期绑定(eraly binding),绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;

动态绑定:又名后期绑定(late binding),绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;


比如常见的,virtual函数是动态绑定,non-virtual函数是静态绑定,缺省参数值也是静态绑定.Virtual函数系动态绑定而来,

思是调用了一个virtual函数时,究竟调用那一份函数实现代码,取决于付出调用的那个对象的动态类型:

pc->draw(shape::Red); //调用Circle::draw(shape::Red)
pr->draw(shape::Red); //调用Rectangle::draw(shape::Red)

其实这些都是老掉牙的知识点,但是今天我们带来了virtual函数不同的地方,缺省值! virtual函数是动态绑定的,而缺省参数

为静态绑定. 意思是你可能会在"调用一个定义与derived class内的virtual函数"的同时,却使用base class为他所指定的缺省

数值.

pr->draw();        //调用Rectangle::draw(shape::Red)!

此例当中,pr的动态类型为Rectangle*,所以调用的是Rectangle的virtual函数,一如你所预料.Rectangle::draw函数的缺省值应

该是Green,但由于pr的静态类型为shape*,所以此一调用的缺省参数值来自shape class而非Rectangle class!结局是这个函数调

用有这奇怪并且几乎没有人预料得到的组合,由shape class和Rectangle class的draw生命式各处一份力.以上事实不只局限于

"ps,pc和pr都是指针"的情况;即使把指针换成references问题依然存在.重点在于draw是一个virtual函数,而它有个缺省参数值在

derived class中被重新定义了.


为什么C++坚持以这种乖张的方式来运作呢? 答案在于运行期效率. 如果缺省参数值为动态绑定,编译器就必须有某种方法在运行

期为virtual函数决定适当的参数缺省值.这比目前实行的"在编译器决定"的机制更慢而且更复杂.为了程序的执行速度和编译器实

现上的简易度,C++做了这样的取舍,其结果就是你如今所享受的执行效率. 但是如果你重新定义了继承而来的缺省参数值,而且偏

偏在缺省参数值上面出现bug,那么你可能需要一个通宵都不一定调的出来.
### C++ 静态绑定动态绑定的概念及区别 #### 1. **静态绑定 (Static Binding)** 静态绑定是在编译期间完成的方法对象之间的绑定过程。这意味着当代码被编译时,编译器已经能够确定应该调用哪个具体的函数或方法[^1]。这种绑定通常用于非虚函数以及不涉及继承关系的简单函数调用场景。 静态绑定的主要特点如下: - 绑定发生在编译期,因此性能较高,因为无需在运行时解析实际调用的对象类型。 - 对象的静态类型决定了所调用的具体函数版本。 - 不支持多态行为,即无论指针或引用指向的实际对象是什么类型,都会按照声明时的静态类型调用对应的函数。 以下是静态绑定的一个示例: ```cpp #include <iostream> using namespace std; class Base { public: void display() { cout << "Base::display()" << endl; } }; class Derived : public Base { public: void display() { cout << "Derived::display()" << endl; } }; int main() { Base b; Derived d; Base* ptr = &b; ptr->display(); // 输出: Base::display() ptr = &d; ptr->display(); // 输出: Base::display() } ``` 在这个例子中,`ptr->display()` 的调用完全由 `Base` 类型决定,即使 `ptr` 实际指向的是 `Derived` 对象,仍然只会调用基类的 `display` 函数[^2]。 --- #### 2. **动态绑定 (Dynamic Binding)** 动态绑定则是指在运行时期间才确定应调用哪一个具体函数的过程。它主要用于处理具有继承结构并定义了虚函数的情况。在这种情况下,最终调用的函数取决于对象的真实类型而非其静态类型[^3]。 为了实现动态绑定C++ 提供了一种特殊的成员函数——**虚函数**(virtual function),并通过一种称为“虚函数表”的内部数据结构来管理这些函数的映射关系。每当创建派生类实例时,相应的虚函数会被加入到该表中,从而使得运行时刻可以根据真实类型的索引来定位正确的函数实现[^4]。 下面是一个关于动态绑定的例子: ```cpp #include <iostream> using namespace std; class Base { public: virtual void display() { cout << "Base::display()" << endl; } // 声明为虚函数 }; class Derived : public Base { public: void display() override { cout << "Derived::display()" << endl; } }; int main() { Base b; Derived d; Base* ptr = &b; ptr->display(); // 输出: Base::display() ptr = &d; ptr->display(); // 输出: Derived::display() } ``` 这里的关键在于 `Base` 类中的 `display` 方法被标记为虚拟 (`virtual`),这允许程序在运行时依据 `ptr` 所指向的实际对象类型去选择合适的重写版 `display` 来执行。 --- ### 总结对比 | 特性 | 静态绑定 | 动态绑定 | |---------------------|-----------------------------------|----------------------------------| | 发生时间 | 编译期 | 运行期 | | 支持多态 | 否 | 是 | | 使用条件 | 没有继承或者未使用虚函数 | 存在继承链且存在至少一个虚函数 | | 效率 | 较高 | 略低 | 尽管动态绑定提供了更大的灵活性支持真正的面向对象特性如多态,但它也引入了一些额外开销,比如维护虚函数表所需的内存空间以及查找合适函数的时间成本。然而,在许多现代硬件架构下,这部分差异几乎可以忽略不计。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值