以下均在64bit linux机器下验证
#include <iostream>
class Bomb
{
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl; //结果为1
}
空类占一个字节
#include <iostream>
class Bomb
{
explicit Bomb(){}
~Bomb(){}
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl; //结果为1
}
有构造函数,晰构函数,还是1
#include <iostream>
class Bomb
{
public:
explicit Bomb(){}
~Bomb(){}
void print(){}
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl; //结果为1
}
加入普通函数,还是1
#include <iostream>
class Bomb
{
explicit Bomb(){}
~Bomb(){}
virtual void print(){}
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl; //结果为8
}
加入虚函数结果为8
#include <iostream>
class Bomb
{
explicit Bomb(){}
~Bomb(){}
virtual void print(){}
virtual void message(){}
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl; //结果为8
}
两个虚函数还是8
#include <iostream>
class Bomb
{
public:
explicit Bomb(){};
virtual void message(const std::string &msg){std::cout << msg << std::endl;}
virtual void print(){std::cout << "bgbbb" << std::endl;}
private:
int a = 3, b = 5;
long c = 10;
};
int main(int argc, char* argv[])
{
Bomb t;
std::cout << sizeof(t) << std::endl;
//虚基表地址在类的首地址,过后就是成员变量
//(long*)&t为对象的地址
//*(long*)&t取出对象地址前8字节的long值,该值为data数据段的虚基表首地址
//(long*)*(long*)&t 将虚基表首地址转换为8字节指针,方便偏移获取具体虚函数
//*((long*)*(long*)&t + 0)取虚基表的第1个8字节long的值,该值就是第一个虚函数的地址,以此类推
using MSG_FUNC = void(*)(Bomb*, const std::string &);
MSG_FUNC f = (MSG_FUNC)*((long*)*(long*)&t + 0);
f(&t, "111111111111111");
using PRINT_FUNC = void(*)(); //没有参数的成员函数,解析获取,可以不带类指针参数
PRINT_FUNC pf = (PRINT_FUNC)*((long*)*(long*)&t + 1);
pf();
using PRINT_FUNC_OBJ = void(*)(Bomb*); //带上指针参数,也可以
PRINT_FUNC_OBJ pfo = (PRINT_FUNC_OBJ)*((long*)*(long*)&t + 1);
pfo(&t);
std::cout << *((long*)&t + 2) << std::endl; //10, long c;
long ab = *((long*)&t + 1); //int a, b
std::cout << (int)(0xFFFFFFFF & (ab)) << std::endl; // a = 3; 大多数机器为小端储存,3在低地址,从偏移量可以看出,偏得越多,地址越高;储存在ab的低位
//取低位4字节就是a
std::cout << *((int*)&ab + 0) << std::endl;
std::cout << *((int*)((long*)&t + 1) + 0) << std::endl;
std::cout << (int)(0xFFFFFFFF & (ab>>32)) << std::endl; // b = 5; 大多数机器为小端储存,3在低地址,从偏移量可以看出,偏得越多,地址越高;储存在ab的低位,5
//在ab的高位,所以右移32位(4个字节)就可以得到b
std::cout << *((int*)&ab + 1) << std::endl;
return 0;
}
结论如下:
1、只要有虚函数,不管多少个,都只有一个虚基表,占8字节(64bit机); 普通成员函数,其它函数不占空间。虚基表在编译后分配在只读数据段
2、成员变量按字节对齐规则分配内存
3、没有成员变量,没有虚函数的空类占一个字节
4、采用指针的方式可以获取类对象的虚函数和成员变量