我正在实现一个非常奇怪的结构,在其中分配一个内存块并在其中存储多个不同类型的对象:
auto memory = reinterpret_cast<std::uintptr_t>(::operator new(size));
new(reinterpret_cast<void*>(memory)) Class1();
new(reinterpret_cast<void*>(memory + offset)) Class2();
我担心的是,这段代码是否违反了严格的别名规则?
如果我按如下方式重写此代码会怎样:
void* memory = ::operator new(size);
new(memory) Class1();
new(reinterpret_cast<void*>(reinterpret_cast<char*>(memory) + offset)) Class2();
鉴于size
保证足够大,内存+偏移量
保证正确对齐,两个类的构造函数都声明为nothrow,并且两个类的析构函数都被调用内存释放后,这段代码是否引入了UB?使用此类代码我还会遇到哪些其他问题?
最佳答案
回答你的问题
My concern, does this code violates the strict aliasing rules?
不,没有
让我们先了解一些事情。
到底什么是别名?
别名是指多个左值引用同一内存位置(当您听到左值时,请考虑可以位于赋值左侧的事物(变量)),即可修改的事物。举个例子:
int anint;
int *intptr=&anint;
为什么首先要引入别名规则?
在引入严格别名之前,编译器必须生活在一种偏执的状态中,即 buff 的内容可以随时随地由任何人更改。因此,为了获得额外的性能优势,并假设大多数人不输入双关指针,引入了严格的别名规则。
因此,在这种设置中,如果我想向某个对象发送消息,我必须有两个不兼容的指针指向同一 block 内存。
正如@Lightness正确提到的
arguably, it's why placement new exists in the first place
Placement new 允许您在已分配的内存上构造对象。您可能希望这样做是为了优化(不要一直重新分配会更快),但您需要多次重新构造对象。如果您需要继续重新分配,分配比您需要的更多可能会更有效,即使您还不想使用它。
What other problems I can encounter with such code? And What if I rewrite this code as follows:
void* memory = ::operator new(size);
new(memory) Class1();
new(reinterpret_cast<void*>(reinterpret_cast<char*>(memory) + offset)) Class2();
请放心,编译器会向您标记一些警告。
注意:为了尽快发现别名问题,-fstrict-aliasing
应始终包含在 GCC 的编译标志中。否则,问题可能仅在最难调试的最高优化级别上可见。
您可能想查看Endianness , Understanding C/C++ Strict Aliasing和 What uses are there for “placement new”?
关于c++ - 使用单个内存块来存储多个对象可以吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48975253/