类的大小
对类求大小实际上即是对类的实体对象求大小,其大小大于或等于所有非静态成员大小的总和(静态成员大小不计入,因为静态成员变量不在对象中存储),超出部分的来源:
- 指向虚函数表的指针vptr,虚函数本身不占空间
- 内存对齐(编译器优化)
说明:虚函数、成员函数(包括静态与非静态)和静态数据成员都不占用类对象的存储空间,与构造和析构无关!
对一个空类Base求sizeof的大小时,sizeof(Base)=1,因为c++标准规定类的大小不为0,空类的大小为1
class Base
{
}
//sizeof(Base)=1
class Base
{
virtual void f(){}
}
//sizeof(Base)=8
虚函数表
同属于一个类的对象共享虚函数表,但有各自的vptr;虚函数表的实质是一个指针数组,里面存的是虚函数的函数指针
- vptr指针在对象内存中存放在开始或末尾,视编译器而定。只需判定对象的地址与对象第一个成员的地址是否相等
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f() {cout<<"base::f"<<endl;}
virtual void g() {cout<<"base::g"<<endl;}
virtual void h() {cout<<"base::h"<<endl;}
};
class Derive : public Base
{
public:
void g() {cout<<"derive::g"<<endl;}
};
typedef void(*Func)(void);
int main(int argc,char* argv[])
{
//这里对象内存仅有指向虚函数表指针的指针,因此取地址直接可以得出该指针
Base iBase;
Base* piDerive = new Derive();
long* ppBasevptr = (long*)&iBase;//指向虚函数表指针的指针
long* pBasevptr = (long*)*ppBasevptr;//虚函数表指针
Func f = (Func)pBasevptr[0];
Func g = (Func)pBasevptr[1];
Func h = (Func)pBasevptr[2];
f();
g();
h();
std::cout << "Basevptr:" << pBasevptr << std::endl;
long* ppDerivevptr = (long*)piDerive;
long* pDerivevptr = (long*)*ppDerivevptr;
Func ff = (Func)pDerivevptr[0];
Func gg = (Func)pDerivevptr[1];
Func hh = (Func)pDerivevptr[2];
ff();
gg();
hh();
std::cout << "Derivevptr:" << pDerivevptr << std::endl;
return 0;
}
/*result:
base::f
base::g
base::h
Basevptr:0x601db0
base::f
derive::g
base::h
Derivevptr:0x601d70
结论:只有被继承的虚函数虚函数表才被覆盖*/
参考
虚函数在虚拟存储器中的位置:
说明:当调用虚函数时,首先通过位于栈区的实例的指针找到位于堆区中的实例地址,然后通过实例内存开头处的vptr找到位于.rodata段的vtbl,再根据偏移量找到想要调用的函数地址,最后跳转到代码段中的函数地址执行目标函数