C++ 多态(迟绑定)原理

今天看到一篇帖子关于虚函数的问题,原以为自己已经对c++虚函数,多态了解的不错了,但是,还是发现有一些模糊点,所以,又回头重新总结了一番。

有了不小的收获,下面,就和大家一起来分享一下,也希望对一些朋友起到一定的帮助。

首先先梳理一下概念,以前似乎对概念不是很重视,其实不然,真正对一个东西的定义了解了很透,才能去深入挖掘这个东西的内涵。

好,首先,什么是绑定?( 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.
迟绑定发生在运行时,基于不同类型的对象。当一种语言实现迟绑定时,必须有某种机制确定对象的具体类型然后调用合适的成员函数。

好,下面不多啰嗦,上实例:(环境:XP3 + VS2005,)

下面的注释部分是调试时,监视窗口的结果

#include <iostream>
using namespace std;

 

class Base
{
public:
virtual void sleep() { cout << "Base::sleep()" << endl;}
};

//两个派生类Derive和Derive1
class Derive : public Base
{
public:
void sleep() { cout << "Derive::sleep()" << endl; }
};

class Derive1 : public Base
{
public:
void sleep() { cout << "Derive1::sleep()" << endl; }
};

int main()
{
Base b;

Base* pb = &b; //
pb->sleep();
printf("pb = %#x\n", pb);

//- pb 0x0012ff60 Base *
// - __vfptr 0x0041770c const Base::`vftable' *
      // [0] 0x0041119f Base::sleep(void) *

Derive d1;
pb = reinterpret_cast<Base*>(&d1);
pb->sleep();
printf("pb = %#x\n", pb);

Derive d2;
pb = reinterpret_cast<Base*>(&d2);
pb->sleep();
printf("pb = %#x\n", pb);

//d1和d2的VPtr的值是相同的,如下:
//- pb     0x0012ff48               Base *
      // - [Derive] {...}        Derive
        // - Base {...}        Base
        // - __vfptr 0x00417714 const Derive::`vftable' * //Base类vfptr(虚函数表指针)
                        // [0] 0x0041128a Derive::sleep(void) *
        // - __vfptr 0x00417714 const Derive::`vftable' * //Derive类vfptr(虚函数表指针)
                           // [0] 0x0041128a Derive::sleep(void) *
//观察到对于Derived类的vfptr指针和Base类的vfptr指针的值是相同的,
//都指向const区中的首地址为0x00417714的vftable(虚函数表)

Derive1 d3;
pb = reinterpret_cast<Base*>(&d3);
pb->sleep();
printf("pb = %#x\n", pb);

//- pb 0x0012ff30 Base *
// - [Derive1] {...} Derive1
    // - Base {...} Base
          // - __vfptr 0x0041773c const Derive1::`vftable' * //Base 类vfptr(虚函数表指针)
                 // [0] 0x00411294 Derive1::sleep(void) *
           // - __vfptr 0x0041773c const Derive1::`vftable' * //Derive1 类vfptr(虚函数表指针)
                 // [0] 0x00411294 Derive1::sleep(void) *

//可以看到:此时,Derive1类的虚函数表指针和上面的Derive类的虚函数表指针的值是不同的

return 0;
}

 


 

总结如下:

一、对于含有虚拟函数的类来说:
1、虚函数表是与类关联的,也就是说,一个类只有一张虚函数表。(从这个方面上说,虚函数类似于类的静态函数)
2、VTable中虚函数的位置是由基类决定的,所以,哪怕你在派生类中不是按基类的顺序来重写虚函数,派生类的VTable中虚函数的存放顺序和基类的也是一样的。
3、虚函数指针VPtr是与对象关联的,每个对象都有一个虚函数指针,但是,同类的对象的虚函数指针的值是相同的。因为都指向该类的虚函数表。
4、虚函数指针VPtr的值是在调用构造函数时进行初始化的。如果该类没有构造函数,那么,默认构造函数干的唯一一件事就是初始化VPtr。所以,当你在写一个类的构造函数时,一定不要写一个空的默认构造函数。


二、C++迟绑定实现机理(编译器都是类似于此种方式实现的):
1、为每个含有虚函数的类(基类以及派生类)都创建一张虚函数表(VTable,存储于常量区),依次存放虚函数的地址。对于派生类来说,如果没有重写其基类的虚函数,那么,将会在VTable中存放基类的虚函数地址。
2、为每个含有虚函数的类的对象,创建一个指针(VPtr),指向这个类的虚函数表(所以说,同类对象的
VPtr的值是一样的,也就是说,虚函数,在这一点上,类似于类的static函数,是所有对象共有的。)
3、通过强制转换将派生类对象的地址/引用 赋给基类指针/变量(也就是向上类型转换UpCasting)

4、然后,通过基类指针/引用对象 来调用虚函数(即:polymorphic call),就会通过此时基类对象的VPtr指针在所指向虚函数表中寻址(一般是从表头地址开始,加上一定的偏移量)找到相应的函数地址,也就是之前派生类VPtr指针指向的虚函数表的函数地址。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/beckle_ye/archive/2009/10/19/4698431.aspx


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值