迟绑定早绑定

1、        概念
首先,什么是绑定?( what`s the definition of binding? )

c++编程思想上有说到:
Connecting a function call to a function body is called binding.(将函数体和函数调用关联起来,就叫绑定)
然后,那么什么是早绑定?(Early binding)
When binding is performed before the program is run (by the compiler and linker), it' s called early binding
在程序运行之前(也就是编译和链接时),执行的绑定是早绑定。
然后,什么是迟绑定?(late binding)
late binding, which means the binding occurs at runtime, based on the type of the object.
When a language implements late binding, there must be some mechanism to determine the

type of the object at runtime and call the appropriate member function

迟绑定发生在运行时,基于不同类型的对象。当一种语言实现迟绑定时,必须有某种机制确定对象的具体类型然后调用合适的成员函数。
2、         结论
2.1对于含有虚拟函数的类来说:
Ø        虚函数表是与类关联的,也就是说,一个类只有一张虚函数表。(从这个方面上说,虚函数类似于类的静态函数)(父类有一个Vtable,子类也有一个Vtable)
Ø        VTable中虚函数的位置是由基类决定的,所以,哪怕你在派生类中不是按基类的顺序来重写虚函数,派生类的VTable中虚函数的存放顺序和基类的也是一样的。
Ø        虚函数指针VPtr是与对象关联的,每个对象都有一个虚函数指针,但是,同类的对象的虚函数指针的值是相同的。因为都指向该类的虚函数表。
Ø        虚函数指针VPtr的值是在调用构造函数时进行初始化的(自动完成)。

2.2 C++迟绑定实现机理流程(编译器都是类似于此种方式实现的):
(1)为每个含有虚函数的类(基类以及派生类)都创建一张虚函数表(VTable,存储于常量区),依次存放虚函数的地址。对于派生类来说,如果没有重写其基类的虚函数,那么,将会在VTable中存放基类的虚函数地址。
(2)为每个含有虚函数的类的对象,创建一个指针(VPtr),指向这个类的虚函数表(所以说,同类对象的VPtr的值是一样的[*VPtr相同,但VPtr不同],也就是说,虚函数,在这一点上,类似于类的static函数,是所有对象共有的。)(此过程发生在运行期自动调用构造函数的过程中)
(3)通过强制转换将派生类对象的地址/引用 赋给基类指针/变量(也就是向上类型转换UpCasting)(此步决定了基类指针首先寻址的虚函数表,也就是现在派生类的虚函数表中寻址,如果在派生类的虚函数表中没有找到此虚函数,则向上一级基类的虚函数表中寻找此虚函数,依次类推。由此可见,虽然代码相同,却实现了调用不同类函数的目的,也就是多态)
(4)然后,通过基类指针/引用对象 来调用虚函数(即:polymorphic call),就会通过此时基类对象的VPtr指针在所指向虚函数表中寻址(一般是从表头地址开始,加上一定的偏移量)找到相应的函数地址,也就是之前派生类VPtr指针指向的虚函数表的函数地址。
 

2.3晚绑定原理
编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数的类中,编译器秘密地置一指针,称为v p o i n t e r(缩写为V P T R),指向这个对象的V TA B L E。通过基类指针做虚函数调用时(也就是做多态调用时),编译器静态地插入取得这个V P T R,并在V TA B L E表中查找函数地址的代码,这样就能调用正确的函数使晚捆绑发生。为每个类设置V TA B L E(编译期)、初始化V P T R(运行期构造函数中)、为虚函数调用插入代码(运行期多态调用时),所有这些都是自动发生的,所以我们不必担心这些。利用虚函数,这个对象的合适的函数就能被调用,哪怕在编译器还不知道这个对象的特定类型的情况下。(《C++编程思想》)
2.4早绑定和晚绑定优缺点
早绑定,晚绑定,早晚都要绑定。绑定指在对象和其类型间建立关联的过程。
早绑定指在对象申明的时候就和他的类型建立了关联。
晚绑定是指我们的代码在运行时再检查对象是否提供了我们所需要的方法和属性。
 很多人说C#和Java是早绑定的,javascript,python是迟绑定的,其实这个说法并不全对
一方面,C#和Java是强类型的,在变量声明的时候就说明了类型,从这里来讲当然是早绑定的,于是我们才能在IDE中享受代码提示带来的方便,因为ide通过你的申明就能知道你的对象是什么类型,具有什么方法和属性,然后提示给你,同时编译的时候也可以帮你检查许多类型转换的错误.

另一方面:无论C#和Java在实现他们很重要的一个功能:多态的时候,都是用晚绑定,比如你的父类中定义了virtual的方法,那么这个方法可能会在你的子类中重载,具体你用什么子类,是变量申明时所不知道的,在C++编译器会在编译的时候为这些类加上一个指针,指针指向一个虚表,虚表中存在着真实的函数,这个是就是一个晚绑定了
早绑定的优点是:

Ø        编译效率

Ø        代码提示(代码智能感知)

Ø        编译时类型检查

晚绑定的优点是:

Ø        不用申明类型

Ø        对象类型可以随时更改

参考资料:
http://blog.youkuaiyun.com/faithmy509/archive/2009/05/21/4207625.aspx

http://www.cnblogs.com/yizhu2000/archive/2007/07/12/815628.html

http://blog.sina.com.cn/s/blog_63797eb20100gni8.html(C++ 多态迟绑定原理)

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/braveyly/archive/2010/05/03/5549978.aspx

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/braveyly/archive/2010/05/03/5549978.aspx

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/braveyly/archive/2010/05/03/5549978.aspx

– 来自百度多态其实可以简单看成就是用户执行同样的行为,但是因为执行者的差异,展现出不同的效果通常语言中会使用继承和动态绑定来实习这一目的(当然,你个年轻人不讲武德,用函数绑定来干这一件事情另谈)常规多态class A {public: virtual void f() {std::cout << "A\n";}};class B: public A {public: void f() {std::cout << "B\n";}};class C: public A {public: void f() {std::cout << "C\n";}};void demo(A* a) { a->f();}int main() { demo(new A); demo(new B); demo(new C); return 0;}12345678910111213141516171819202122232425直接使用std::function第二种并算不上一种方法,但是在只有一个两个虚函数时还是有用途的class A {public: std::function<void()> f; explicit A(std::function<void()>&&fun): f(std::move(fun)){}};void demo(A* a) { a->f();}int main() { demo(new A([]() {std::cout << "A\n";})); demo(new A([]() {std::cout << "B\n";})); demo(new A([]() {std::cout << "C\n";}));}123456789101112131415上述都是绑定c++里面,若是要实现多态的话,实现多态利用的虚表来实现的绑定(类似于std::function),虚表中存放好对应函数的调用地址,以此来实现运行时选择但是,运行时绑定也不是没有负担C++中内存布局函数不能内联和优化虚表再怎么都需要占用一个指针的空间,不然,如何存放虚表指针由于是运行时决定,而不是绑定,需要通过虚表去寻找调用地址,中间存在一个过程(虽然可以忽略不计,但是不计较今天怎么说CRTP呢?)。c++的虚表可以满足需求了c++是一门多样化的语言多样化让c++变得可以选择的方式变多了(比如上述的直接利用std::function来搞,你只管实现,剩下的交给编译器)CRTP,一般类似于这种形式template<typename T>class A {public: T* self() {return static_cast<T*>(this);} };class B: public A<B>{ };123456789核心在于,父类是个模板,模板有个参数是子类对于父类来说,只需要知道子类的名称可以生成一个指针就可以了父类在实例化时只需要有子类的声明的就ok了,由于传入的模板类会继承自己,所以自己可以向下转这个指针。于是,我们就可以这样玩:template <typename D> class Base { void f_impl(); int add_impl(int a, int b); D *self() { return static_cast<D *>(this); }protected: Base() {}public: void f() { self()->f_impl(); } int add(int a, int b) { return self()->add_impl(a, b); }};class A : public Base<A> {public: int x; A(int v): x(v) {} int add_impl(int a, int b) {return x + a + b;} void f_impl() { std::cout << "A\n"; }};class B : public Base<B> {public: int x; B(int v): x(v) {} int add_impl(int a, int b) {return x + a + b;} void f_impl() { std::cout << "B\n"; }};int main() { Base<A> *t = new A(1); t->f(); std::cout << t->add(1, 2) << "\n"; Base<B> *p = new B(1); p->f(); std::cout << p->add(1, 2) << "\n"; return 0;}12345678910111213141516171819202122232425262728293031323334353637不让父类生成对象父类只声明具体函数实现其实只是交给编译器,去codegen 生成了 几分不同的 base <T> 的类所有行为发生在编译期上述main代码里面还是重复代码了,我们是“多态”,当然要做一个统一的动作呢统一动作呢,当然也要统一生成
最新发布
04-04
### C++中的多态实现方式 C++支持两种主要的多态形式:静态多态(编译期多态)和动态多态(运行时多态)。 - **静态多态**通常由函数重载和模板机制提供,在编译阶段完成绑定。这种多态不涉及任何运行时开销。 - **动态多态**则依赖于虚函数机制,允许通过基类指针或引用调用派生类的方法[^2]。 #### 虚函数与动态绑定 虚函数是C++中实现动态多态的核心工具之一。它利用虚拟表(vtable)来存储指向具体方法的地址列表,从而实现在运行时决定调用哪个版本的函数。这种方式虽然灵活,但在每次调用虚函数时都需要额外查找操作,因此会产生一定的性能开销[^3]。 --- ### CRTP模式及其工作原理 CRTP(Curiously Recurring Template Pattern)是一种基于模板元编程的技术,用于在编译期间实现类似于继承的行为。其基本思想是让派生类作为模板参数传递给基类,从而使基类能够访问派生类的成员和类型定义[^1]。 以下是CRTP的一个典型例子: ```cpp template<typename Derived> class Base { public: void interface() { static_cast<Derived*>(this)->implementation(); } }; class Derived : public Base<Derived> { private: friend class Base<Derived>; void implementation() { // 具体实现逻辑 } }; ``` 在这个例子中,`Base` 类通过 `static_cast` 将自己转换为具体的派生类类型,并调用该类型的私有方法 `implementation()`。由于整个过程发生在编译期,所以不需要借助虚函数表或其他运行时机制,也就避免了相应的性能损失。 --- ### 编译期绑定 vs 运行时绑定 | 特性 | 编译期绑定 (CRTP) | 运行时绑定 (虚函数) | |---------------------|--------------------------------------------|--------------------------------------| | **绑定时间** | 编译器解析 | 程序执行过程中 | | **灵活性** | 不支持完全未知的子类 | 支持任意数量的未预见子类 | | **性能影响** | 几乎无额外成本 | 存在 vtable 查找带来的轻微延 | | **适用场景** | 性能敏感场合 | 设计复杂、扩展性强的应用框架 | 对于高性能需求的场景,比如数值计算库 Eigen 或通用算法库 Boost,更倾向于采用 CRTP 来减少不必要的运行时开销。 --- ### 结论 综上所述,C++ 提供了多种手段来实现多态行为。如果目标是最优性能,则应优先考虑像 CRTP 这样的技术;而当系统的重点在于可维护性和扩展能力时,传统的虚函数可能是更好的选择。最终的选择取决于实际应用的需求权衡。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值