这里有人用过c++的“placement new”吗?如果有,为什么?在我看来,它只在内存映射硬件上有用。
当前回答
它被std::vector<>使用,因为std::vector<>通常分配比vector<>中的对象更多的内存。
其他回答
实际上,实现任何类型的数据结构都需要分配比插入元素数量的最低要求更多的内存(即,除了每次分配一个节点的链接结构之外的任何数据结构)。
以unordered_map、vector或deque等容器为例。这些都为您插入的元素分配了比最低要求更多的内存,以避免为每次插入都需要堆分配。让我们用向量作为最简单的例子。
当你这样做时:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... 其实也造不出一千个foo。它只是为它们分配/保留内存。如果vector没有在这里使用放置new,它将在所有地方默认构造foo,并且必须调用它们的析构函数,即使是对于你从未在第一个位置插入的元素。
分配=建设,解放=毁灭
一般来说,要实现像上面这样的许多数据结构,不能将分配内存和构造元素视为一个不可分割的事情,同样也不能将释放内存和销毁元素视为一个不可分割的事情。
为了避免不必要地向左或向右调用多余的构造函数和析构函数,这就是标准库将std::allocator(在分配/释放内存*时不构造或销毁元素)的思想与使用它的容器分开的原因,这些容器使用放置new手动构造元素,使用显式调用析构函数手动销毁元素。
我讨厌std::allocator的设计,但这是一个不同的主题,我将避免咆哮。: - d
So anyway, I tend to use it a lot since I've written a number of general-purpose standard-compliant C++ containers that could not be built in terms of the existing ones. Included among them is a small vector implementation I built a couple decades ago to avoid heap allocations in common cases, and a memory-efficient trie (doesn't allocate one node at a time). In both cases I couldn't really implement them using the existing containers, and so I had to use placement new to avoid superfluously invoking constructors and destructors on things unnecessary left and right.
当然,如果你曾经使用自定义分配器来单独分配对象,比如一个free list,那么你通常也会想使用placement new,就像这样(基本的例子,不需要担心异常安全或RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);
我曾看到它被用作“动态类型”指针的轻微性能hack(在“引擎盖下”一节中):
但这是我用来获得小类型的快速性能的棘手技巧:如果所持有的值可以放入void*中,我实际上不需要分配一个新对象,而是使用placement new将其强制到指针本身。
我使用它来构造通过alloca()分配到堆栈上的对象。
无耻的宣传:我在这里写过博客。
放置new允许在已经分配的内存中构造一个对象。
当您需要构造一个对象的多个实例时,您可能需要这样做以进行优化,并且在每次需要新实例时不重新分配内存会更快。相反,对可以容纳多个对象的内存块执行一次分配可能会更有效,即使您不想一次使用所有内存块。
DevX给出了一个很好的例子:
标准c++也支持放置 New操作符,它构造 对象在预先分配的缓冲区上。这 在构建内存池时很有用, 垃圾收集器或简单的什么时候 性能和异常安全是重要的 派拉蒙(没有危险 内存分配失败 已经分配了,然后呢 对象上构造对象 预分配缓冲区占用的时间更少):
char *buf = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi"); // placement new
string *q = new string("hi"); // ordinary heap allocation
您可能还希望确保在关键代码的特定部分(例如,在由起搏器执行的代码中)不存在分配失败。在这种情况下,您可能希望更早地分配内存,然后在临界区中使用placement new。
脱销在安置新
不应该释放正在使用内存缓冲区的每个对象。相反,您应该只删除[]原始缓冲区。然后必须手动调用类的析构函数。关于这方面的建议,请参阅Stroustrup的常见问题:是否存在“位置删除”?
我认为任何答案都没有强调这一点,但新位置的另一个好例子和用法是减少内存碎片(通过使用内存池)。这在嵌入式和高可用性系统中特别有用。在最后一种情况下,这一点特别重要,因为对于一个必须运行24/365天的系统来说,没有碎片是非常重要的。此问题与内存泄漏无关。
Even when a very good malloc implementation is used (or similar memory management function) it's very difficult to deal with fragmentation for a long time. At some point if you don't manage cleverly the memory reservation/release calls you could end up with a lot of small gaps that are difficult to reuse (assign to new reservations). So, one of the solutions that are used in this case is to use a memory pool to allocate before hand the memory for the application objects. After-wards each time you need memory for some object you just use the new placement to create a new object on the already reserved memory.
这样,一旦应用程序启动,就已经预留了所需的所有内存。所有新的内存预留/释放都将分配到已分配的池中(您可能有几个池,每个池对应一个不同的对象类)。在这种情况下不会发生内存碎片,因为没有间隙,您的系统可以运行很长时间(数年)而不会出现内存碎片。
我在实践中看到过这种情况,特别是在VxWorks RTOS中,因为它的默认内存分配系统受到了很多碎片的影响。因此,通过标准的new/malloc方法分配内存在项目中基本上是被禁止的。所有的内存预留都应该到一个专用的内存池中。
推荐文章
- 什么是“参数依赖查找”(又名ADL,或“Koenig查找”)?
- 公共朋友交换成员函数
- 如何在Go中使用c++
- 自定义c++分配器的引人注目的例子?
- RAII和c++中的智能指针
- 如何构建和使用谷歌TensorFlow c++ api
- 断言是邪恶的吗?
- 下面这些短语在c++中是什么意思:0 -,default-和value-initialization?
- 在STL地图中,使用map::insert比[]更好吗?
- C++ Linux的想法?
- 如何为Fedora安装g++ ?
- Std::cin输入空格?
- c++标准是否要求iostreams的性能很差,或者我只是在处理一个糟糕的实现?
- gcc在哪里查找C和c++头文件?
- 为什么我们需要require require ?