给定一个指向原始内存“blob”的void指针,有两种方法可以将内容写入其中。
第一种方法是使用新的展示位置。这种方法的优点是在处理类类型时自动调用ctor。但是,当我处理非类型类型时,做一个强制转换会更好吗?我想它可能会更快。
(pLocation是一个指向内存blob的空指针
// ----- Is this better -----
*reinterpret_cast<char*>(pLocation) = pattern;
// ----- Or is this better -----
::new(pLocation) char(pattern);
答案 0 :(得分:3)
虽然将原始内存转换为对象可能在实践中有效,但正式它会调用未定义的行为,因此,根据C ++标准,您的代码可能会执行任何操作。
Placement new,OTOH,是一种在特定地址调用构造函数的技术,而构造正式将原始内存转换为有效对象。这就是为什么我更喜欢新的位置。
只是为了确保,我也会调用这些对象的析构函数。虽然你说你只需要这个用于POD和POD的破坏是一个无操作,我在我的运营商中看到的许多错误都是在编写时考虑了一系列限制的代码,但后来有一些限制解除了并突然发现自己处于一个无法应付的环境中。
另请注意,可能存在平台,即使对于内置类型,并非所有可能的位模式都是有效值。此类平台也可能会访问此类模式的值。例如,可能是全零位模式不是浮点类型的有效值,因此即使将前面的内存归零也无法阻止硬件异常。
答案 1 :(得分:3)
我使用以下程序查看了每种技术的生成程序集:
#include <new>
char blob[128];
int main() {
void *pLocation = blob;
char pattern = 'x';
#ifdef CAST
*reinterpret_cast<char*>(pLocation) = pattern;
#else
::new(pLocation) char(pattern);
#endif
}
我在Linux 64位上使用带有默认编译器标志的g ++ 4.4.3。
asm的相关部分展示位置:
movb $120, -1(%rbp)
movq -16(%rbp), %rax
movq %rax, %rsi
movl $1, %edi
call _ZnwmPv
movq %rax, %rdx
testq %rdx, %rdx
je .L5
movzbl -1(%rbp), %edx
movb %dl, (%rax)
.L5:
从我收集的内容中,这实际上调用了placement new运算符,并检查其返回值,即使它总是成功。然后它继续将x
的值写入返回的内存中。
对于 reinterpret_cast
:
movb $120, -1(%rbp)
movq -16(%rbp), %rax
movzbl -1(%rbp), %edx
movb %dl, (%rax)
请注意,这些说明与展示位置new
的前两个和最后两个相同。
使用-O1
,两段代码生成相同的程序集:
movb $120, blob(%rip)
所以,如果你担心表现,不要。任何其他理智的编译器都可能将两者都减少到相同的代码。