静态绑定和🍔动态绑定
大约 4 分钟
静态绑定和动态绑定
静态绑定
静态指的是编译时期
绑定即函数调用
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
Base(int data = 10): ma(data){}
void show()
{
cout << "Base::show()" << endl;
}
void show(int)
{
cout << "Base::show(int)" << endl;
}
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data = 20):Base(data),mb(data){}
void show()
{
cout << "Derive::show()" << endl;
}
private:
int mb;
};
int main()
{
Derive d(50);
Base *pb = &d;
pb->show();
pb->show(10);
cout << sizeof(Base) << endl;//4
cout << sizeof(Derive) << endl;//8
cout << typeid(pb).name() << endl;//class Base * //该指针的类型,定义的是Base*,所以打印也是Base*
cout << typeid(*pb).name() << endl;//class Base //解引用,表示指针指向的类型
return 0;
}
pb是基类类型, 调用的是派生类从基类继承来的方法

pb->show();//静态绑定 call Base::show(01612DAh)
pb->show(10);//静态绑定 call Base::show(01612B2h)
编译期间将高级源代码编译为汇编码,指定了call Base::show(01612DAh)
与call Base::show(01612B2h)
,即编译期间指定了函数的调用,这就是是静态绑定。

动态绑定
class Base
{
public:
Base(int data = 10): ma(data){}
virtual void show()//虚函数
{
cout << "Base::show()" << endl;
}
virtual void show(int)//虚函数
{
cout << "Base::show(int)" << endl;
}
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data = 20):Base(data),mb(data){}
void show()
{
cout << "Derive::show()" << endl;
}
private:
int mb;
};


虚函数表里的“0”,表示的是虚函数指针vfptr在虚函数表里的偏移量
Derive d(50);
Base *pb = &d;
pb->show();
pb->show(10);
🍗🍗🍗pb依然是Base类型的指针,指向的是派生类对象,编译阶段编译器先去Base作用域看一下Base::show()
- 若是普通函数,则进行静态绑定,
call Base::show()
- 若是虚函数,则进行动态绑定,
mov eax,dword ptr[pb] #将派生类对象的前4个字节(虚函数表的地址vfptr)放入eax寄存器
mov ecx,dword ptr[eax] #将vfptr存的地址(派生类虚函数表)的前4字节内存&Derive::show()地址放入ecx
call ecx #调用ecx,取虚函数地址
最终call 一个寄存器,只有在运行时候才知道寄存器的地址,找到哪个地址调用哪个函数,这就是动态绑定
sizeof变化的原因:
多了virtual,即虚函数会多vfptr指针,因此sizeof()大小也会变。
*pb
的类型:typeid()
- 如果Base没有虚函数,
pb
是*Class Base
;*pb
识别的就是编译时期的类型,*pb
就是Class Base
- 如果Base有虚函数,
pb
是*Class Base
;*pb
识别的就是运行时期的类型:RTTI类型,即Class Derive
RTTI运行时的类型信息
存储的时候字符串类型,表示该虚函数表是由什么类类型产生的
RTTI应用:可以通过typeid()判断两个类是否属于同一个类
C++ RTTI介绍及使用示例(run-time type Identification)_rtti 使用-CSDN博客
vs 查看内存分布

cmd找到源文件所在目录
输入下面命令
cl -d1reportSingleClassLayout(输出对象内存布局信息,类的名字)

注意:在虚函数表中,派生类重写的函数的入口地址应该在基类的函数入口地址的下面,基类被重写的函数的入口地址应该是直接不用了,派生类的放在基类后
总结
静态指的是编译时期
动态是运行时期
绑定指的是函数调用
动态绑定一定是对虚函数的绑定
因此:
静态绑定会生成一个指令,call一个具体函数地址
动态绑定:若指针指向的是虚函数,则根据派生类前四个字节获取获取虚函数表指针,进一步获取虚函数表中虚函数地址,指令上是call一个寄存器,寄存器放的地址,只能运行时才能知道