静态绑定
class Base
{
public:Base(int data) :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(); // 静态绑定,编译时期类型Basepb->show(10);cout << sizeof(Base) << endl; //4cout << sizeof(Derive) << endl;//8cout << typeid(pb).name() << endl; // class Base*cout << typeid(*pb).name() << endl; // class Basereturn 0;
}
虚函数
一个类添加了虚函数,对这个类有什么影响?
- 如果类里定义了虚函数,那么编译阶段,编译器给这个类产生一个唯一的vftable虚函数表,主要存储的内容就是RTTI(Runtime Type Identification)指针和虚函数地址。程序运行时,每张虚函数表都会加载到内存的
.rodata
段 - 一个类里面定义了虚函数,那么定义该类对象时,程序运行时,内存中开始部分,多存储一个虚函数表指针(虚函数表指针放在类存储的最前面4个字节),指向相应类型的虚函数表vftable.
- 一个类型定义个n个对象,它们都要各自的vfptr,指向的是同一张虚函数表
- 虚函数表编译时生成,运行时放到只读数据段(
.rodata
) - 一个类里虚函数个数,不影响内存大小(都只是存一个vfptr),影响的只是虚函数表大小
其中&RTTI下面的0表示RTTI指针在整个对象内存空间的偏移量
如果派生类中的方法,和基类继承来的方法,返回值、函数名、参数列表都相同,而且基类方法是virtual
,那么派生类的这个方法,自动处理成虚函数,完成了重写(或称为覆盖)
class Derive : public Base
{
public:Derive(int data = 20) :Base(data), mb(data) {}void show() { cout << "Derive::show()" << endl; }
private:int mb;
};
子类对象虚函数表如下图
静态绑定时,汇编直接就是调用具体函数
如果是动态绑定,则汇编不会call具体的函数而是虚函数表中的虚函数地址,部分汇编指令
Derive d(50);
Base* pb = &d;
pb->show(); // 检测到函数是虚函数,动态绑定,调用Derive::show()
pb->show(10);cout << sizeof(Base) << endl; // 8 (多了vfptr)
cout << sizeof(Derive) << endl; // 12cout << typeid(pb).name() << endl; // class Base *
cout << typeid(*pb).name() << endl; // class
pb的类型如何确定?
若Base
无虚函数,*pb识别的就是编译期类型即Base
如果Base
有虚函数,就要去其对应虚函数表中查RTTI
指针,得出运行时的类型
可以通过visual studio–》【工具】–》【命令行】查看类的对象模型
cl extend01.cpp /d1reportSingleClassLayoutDerive
基类的对象模型如下
只要基类有虚函数,派生类是否重写虚函数都会生成vfptr
和vftable
(同一个类的所有对象只有一张表),基类有虚函数时,虚表指针是从基类带来的
要点
- 虚函数的调用不一定是动态绑定
如,在类的构造函数中调用虚函数也是静态绑定,因为对象未建立,虚函数表还没生成。
- 用对象本身调用虚函数是静态绑定,只有用指针或引用指向一个对象来进行调用虚函数才可能发生动态绑定.