C++ 空/非空、虚/非虚类的sizeof内存大小规则答疑
测试背景与代码
我尝试探究不同类型类的内存占用大小,分别测试空类/非空类、含虚函数/不含虚函数类的sizeof取值,测试代码如下:
#include <iostream> class empty_virtual_base { empty_virtual_base() {} virtual ~empty_virtual_base() {} }; class non_empty_virtual_base { int a; non_empty_virtual_base() {} virtual ~non_empty_virtual_base() {} }; class empty_base { empty_base() {} ~empty_base() {} }; class non_empty_base { int a; non_empty_base() {} ~non_empty_base() {} }; int main() { std::cout << "empty virtual class: " << sizeof(empty_virtual_base) << "bytes" << std::endl; std::cout << "non empty virtual class: " << sizeof(non_empty_virtual_base) << "bytes" << std::endl; std::cout << "empty normal class: " << sizeof(empty_base) << "bytes" << std::endl; std::cout << "non empty normal class: " << sizeof(non_empty_base) << "bytes" << std::endl; std::cin.get(); return 0; }
程序运行输出:
empty virtual class: 4 bytes non empty virtual class: 8 bytes empty normal class: 1 bytes non empty normal class: 4 bytes
已知不含虚函数的空类会分配1字节内存,用于唯一标识类的实例地址,测试中存在两点疑问:
- 含虚函数的空类大小为4字节,这4字节是指向虚表的vptr指针占用的,为什么这类不需要像普通空类一样额外分配1字节做实例标识?
- 当类中添加占4字节的int类型成员变量后,含虚函数的类大小为8字节,不含虚函数的类大小为4字节;按之前的认知非虚类应该是1字节占位加4字节int共5字节,为什么原本的1字节占位没有体现?
核心规则说明
要搞懂这两个问题,先记住两个最基础的C++对象内存规则:
- 所有不同的对象实例,内存地址绝对不能重复。如果一个类没有任何实际存储数据的内容——没有自定义的非静态成员变量、没有虚函数、也没有继承自带成员/虚函数的基类,编译器才会隐式添加1字节的占位,保证声明两个同类对象时地址不冲突。
- 只要类里已经存在实际占用内存的内容——不管是开发者自己写的成员变量,还是编译器隐式插入的虚表指针
vptr,上述的1字节占位就不会被添加。同时类的内存排布需要满足对齐要求,最终总大小必须是类内最大对齐值的整数倍。
疑问解答
为什么带虚函数的空类不需要额外1字节占位?
带虚函数的类,编译器会为每个实例隐式插入一个指向虚函数表的指针vptr,在你测试的32位环境下,指针本身就占4字节。
此时类的实例已经有实打实的4字节内存占用,靠这部分内存已经可以唯一区分不同实例,完全满足地址不重复的要求,自然不需要再多塞1字节占位。如果强行添加这1字节,还会因为指针的4字节对齐要求,额外补3字节填充,平白让类大小涨到8字节,属于完全没必要的内存浪费,编译器不会做这种设计。
为什么加了int成员后,非虚类没有算上1字节占位?
你认知里的「1字节占位+4字节int共5字节」是错误的:那个1字节占位只有在类完全没有任何实际数据成员的时候才会存在。
当你给普通空类添加int类型的非静态成员后,类已经有了4字节的实际数据存储,这部分内存已经足够区分不同实例,那个为了空类准备的1字节占位从一开始就不会被插入,自然不会算入总大小。
而含虚函数的类最终大小为8字节也符合规则:4字节vptr加上4字节int成员,两者的对齐值都是4,加总后刚好是8,满足对齐要求不需要额外填充,因此结果为8字节。
补充:当前测试结果是32位编译环境下的数值,如果切换到64位环境,指针长度为8字节,对应带虚函数的空类大小会是8字节,带int成员的虚类大小会是16字节(8字节vptr + 4字节int + 4字节对齐填充)。
内容的提问来源于stack exchange,提问作者Y. Luo

