代码优化和对象内存布局不遵循相同的规则
C++ 标准对对象的内存布局规定了以下内容:
1.8/2:对象可以包含其他对象,称为子对象。子对象可以是成员子对象、基类子对象或数组元素。(...)
9.2/13:分配具有相同访问控制的(非联合)类的非静态数据成员,以便以后的成员在类对象中具有更高的地址。未指定具有不同访问控制的非静态数据成员的分配顺序。实现对齐要求可能会导致两个相邻的成员不会被立即分配;管理虚拟功能和虚拟基类的空间需求也是如此。
这保证了对象中包含非静态 const 成员(它们是数据成员,即使它们是 const 也是如此)。所以编译器不允许缩短对象的大小。
但是,只要不改变可观察行为,编译器就有权执行代码优化,例如常量传播和死代码消除、重新排序等:
1.9/5:执行格式良好的程序的一致实现应产生与具有相同程序和相同输入的抽象机的相应实例的可能执行之一相同的可观察行为。(...)
因此,如果您的 const 成员不是volatile
nor atomic<>
,编译器可以很好地生成
A obj(); // size not touched. And const member will be initialized if needed
int absoluteVal = 2; // constant propagation + inlining (the object is not even accessed)
附加信息
这是一个无法优化对象的示例:
A obj(-2); // object is constructed
int absoluteVal = std::abs(obj.constVar); // will be optimized a way into = 2
std::cout<<absoluteVal<<std::endl;
size_t lo = sizeof(obj);
std::cout<<lo<<std::endl;
std::cout.write((char*)&obj, lo); // obj is written to a stream
// and output of content at &obj adress is observable behavior
您可以在线查看优化器结果:尽管计算已absoluteVal
被优化掉,但对象仍以全长实例化并初始化其常量:
...
mov esi, 2 ; this is absoluteVal calculation
mov DWORD PTR [rsp+12], -2 ; the const in [rsp+12] object is nevertheless initialized
...
lea rsi, [rsp+12] ; the address of the object
mov edx, 4 ; and its length
... ; are used to call cout.write()
call std::basic_ostream<char, std::char_traits<char> >::write(char const*, long)
这是因为将这个可简单复制的对象写入流的可观察行为要求对象的每个字节都符合预期。