“placement new” 有哪些用途?

515

这里有人使用过C++的“定位new”吗?如果使用过,是用来做什么的?在我看来,它似乎只对内存映射硬件有用。


21
这正是我一直在寻找的信息,可以在boost分配的内存池上调用对象构造函数。(希望这些关键词能够方便将来的某个人查找到它)。 - Sideshow Bob
3
它被用于C++11维基百科文章中,作为一个联合体的构造函数。 - HelloGoodbye
1
@HelloGoodbye,很有趣!在您所链接的文章中,为什么不能只做 p = pt 并使用 Point 的赋值运算符,而要做 new(&p) Point(pt)?我想知道两者之间的区别。前者会调用 Pointoperator=,而后者会调用 Point 的复制构造函数吗?但我还不是很清楚为什么一个比另一个更好。 - Andrei-Niculae Petre
1
@Andrei-NiculaePetre 我自己没有使用过放置 new,但我猜想如果你当前没有该类的对象,你应该使用它——连同复制构造函数一起使用,否则你应该使用复制赋值运算符。除非该类是平凡的;否则无论你使用哪个都无所谓。同样的事情也适用于对象的销毁。如果对于非平凡的类不能正确处理这个问题,很可能会导致奇怪的行为,甚至在某些情况下可能会导致未定义的行为。 - HelloGoodbye
1
这里有一些优秀的使用演示/示例,以说明如何使用放置new并展示其功能!https://www.geeksforgeeks.org/placement-new-operator-cpp/ - Gabriel Staples
显示剩余6条评论
25个回答

9

当你想要重新初始化全局或静态分配的结构时,这也是很有用的。

旧的 C 的方法是使用 memset() 将所有元素设置为 0。由于虚表和自定义对象构造函数的原因,你不能在 C++ 中这样做。

因此,有时我会使用下面的方法。

 static Mystruct m;

 for(...)  {
     // re-initialize the structure. Note the use of placement new
     // and the extra parenthesis after Mystruct to force initialization.
     new (&m) Mystruct();

     // do-some work that modifies m's content.
 }

1
在这种情况下,重新初始化之前难道不需要进行相应的销毁吗? - Head Geek
通常情况下,您需要这样做。但是有时候,当您知道该类不会分配内存或其他资源(或者您在外部释放了它们 - 例如使用内存池时),您可以使用此技术。它确保v-table指针不会被覆盖。- nimrodm 16小时前 - nimrodm
1
即使在C语言中,将所有位设置为0也只能保证对于整型类型产生0的表示,而不是其他类型(空指针可能具有非零表示)。 - curiousguy
@curiousguy - 对于原始类型,你是正确的(这将使程序可预测,这在调试时是一个优势)。然而,C++数据类型将运行它们的构造函数(就地)并且将被正确初始化。 - nimrodm

9

如果您正在构建内核,那么从磁盘读取的内核代码或页表应该放在哪里是很有用的?您需要知道要跳转到哪里。

或者,在其他非常罕见的情况下,例如当您有大量分配的空间并想将几个结构体放在一起时,这也很有用。它们可以这样打包而无需使用offsetof()运算符。但是还有其他技巧。

我还相信一些STL实现使用放置new,例如std::vector。他们以这种方式为2^n个元素分配空间,不需要总是重新分配。


减少内存分配是使用它的一个主要原因,还有像从磁盘加载对象这样的“技巧”。 - lefticus
我不知道有任何用 C++ 编写的内核;大多数内核都是用纯 C 编写的。 - Adam Rosenfield
8
我学习操作系统基础的时候使用的操作系统是用C++编写的:http://sweb.sourceforge.net - mstrobl

8

我曾经使用它来存储具有内存映射文件的对象。
一个具体的例子是图像数据库,该数据库处理了大量的大型图像(超过内存容量)。


8

std::vector<>使用它是因为std::vector<>通常会分配比vector<>中的objects更多的内存。


7

我曾使用它来基于内存中包含的从网络接收到的消息创建对象。


7

我曾经见过它被用作“动态类型”指针的微小性能优化(在“底层”部分):

但是这里是我用来获得小型类型快速性能的巧妙技巧:如果被保存的值可以适应于void*中,我实际上不需要分配新对象,我使用放置new将其强制放入指针本身。


“如果被保存的值可以适合 void* 中” 是什么意思?任何指针类型都可以分配给 void*。你能给我们举个例子吗? - anurag86
@anurag86:在我的64位机器上,void*占用8个字节。将一个八字节的void*指向一个一字节的bool有点傻。但实际上可以将bool覆盖在void*上,就像union { bool b; void* v }一样。您需要某种方式来知道您所调用的东西是void*实际上是bool(或shortfloat等)。我链接的文章描述了如何做到这一点。而且,为了回答最初的问题,放置new是用于在期望void*的位置创建bool(或其他类型)的功能,(使用强制转换后可获取/修改值)。 - Max Lybbert
@anurag86:虽然不是完全相同的东西,但你可能会对标记指针(https://en.wikipedia.org/wiki/Tagged_pointer)感兴趣。 - Max Lybbert

5

通常,placement new 用于消除“普通 new” 的分配成本。

我使用它的另一个场景是,我想要访问尚未构建的对象的指针,以实现每个文档的单例。



4
我曾经在分配连续缓冲区并根据需要填充对象的容器中遇到过它。如上所述,std::vector可能会这样做,我知道某些版本的MFC CArray和/或CList也是这样做的(因为那是我第一次遇到它的地方)。过度分配缓冲区的方法是非常有用的优化方式,在这种情况下放置new几乎是构造对象的唯一方法。有时也会用于在直接代码之外分配的内存块中构造对象。
虽然不经常出现,但我也在类似的情况下使用过它。这是C++工具箱中一个有用的工具。

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接