1.虚函数
首先,我们看一下当Base*指向Derive对象时,而Base类中含有虚函数时,基类和派生类大小、基类和派生类指针(引用)的类型。
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
Base(int a = 1)
{
ma = a;
}
virtual void show()
{
cout<<"base show 1"<<endl;
}
void show(int b)
{
cout<<"base show 2"<<endl;
}
private:
int ma;
};
class Derive : public Base
{
public:
Derive(int b = 2):Base(b)
{
mb = b;
}
void show()
{
cout<<"derive show 1"<<endl;
}
private:
int mb;
};
int main()
{
Base b;
Derive d;
Base* p = &d;
cout<<"base size:"<<" "<<sizeof(b)<<endl;
cout<<"derive size:"<<" "<<sizeof(d)<<endl;
cout<<"p type:"<<" "<<typeid(p).name()<<endl;
cout<<"*p type:"<<" "<<typeid(*p).name()<<endl;
return 0;
}
结果如下:
基类的大小和父类的大小都增加了4个字节。并且当Base*指向Derive对象时,*Base的类型却变为Derive,不再和指针本身的类型相关,这是怎么回事呢?
2.虚函数指针 vfptr
实际上,Base和Derive类增加的4个字节就是虚函数指针的大小,每一个类只要有虚函数(包括继承而来的),它就有且只有一个虚函数指针,类的大小就是总的成员变量的大小加上一个虚函数指针的大小。虚函数指针指向的是一张虚表,里面是这个类所有虚函数的地址,一个类对应一张虚函数表,而虚函数指针存在于每一个对象中,并且永远占据对象内存的前四个字节。
首先我们先看下Base类(虚函数指针优先级最高)
3. 虚函数表vftable
虚函数表又称为“虚表”,它在编译期间就已经确定,在程序运行时就会被装载到.rodata,在整个程序运行期间都会一直存在。一个类实例化的多个对象,它们的虚函数指针指向的是同一张虚表,一个类拥有一张虚表 。下面是虚函数表的三个信息:
- RTTI:run time type information 运行时类型信息
- vfptr相对于内存布局的偏移
- 虚函数入口地址
以上Base类中虚函数指针vfptr指向的虚函数表——vftable如下所示