有什么用途“放置新的”?
有没有人曾经使用C ++的“新的位置”? 如果是这样,为了什么? 在我看来,它只会在内存映射硬件上有用。
Placement new允许您在已分配的内存上构build一个对象。
您可能想要优化(不要一直重新分配会更快),但是您需要重新构build一个对象多次。 如果您需要重新分配,即使您不想使用它,分配的分配量也可能比您需要的分配效率更高。
Devex给出了一个很好的例子 :
标准C ++也支持放置新运算符,它在预先分配的缓冲区上构build一个对象。 这在构build内存池,垃圾回收器或者仅仅是性能和exception安全性至关重要(没有分配失败的危险,因为内存已经被分配,并且在预分配的缓冲区上构build对象花费较less的时间)时是有用的。 :
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
您可能还想确保在关键代码的某个部分没有分配失败(例如,您可能在起搏器上工作)。 在这种情况下,你会想使用新的位置。
取消分配新的
您不应该释放使用内存缓冲区的每个对象。 相反,你应该删除[]只有原来的缓冲区。 你将不得不手动直接调用你的类的析构函数。 有关此方面的好build议,请参阅Stroustrup的常见问题解答: 是否有“删除位置” ?
我们用它来定制内存池。 只是一个素描:
class Pool { public: Pool() { /* implementation details irrelevant */ }; virtual ~Pool() { /* ditto */ }; virtual void *allocate(size_t); virtual void deallocate(void *); static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ } }; class ClusterPool : public Pool { /* ... */ }; class FastPool : public Pool { /* ... */ }; class MapPool : public Pool { /* ... */ }; class MiscPool : public Pool { /* ... */ }; // elsewhere... void *pnew_new(size_t size) { return Pool::misc_pool()->allocate(size); } void *pnew_new(size_t size, Pool *pool_p) { if (!pool_p) { return Pool::misc_pool()->allocate(size); } else { return pool_p->allocate(size); } } void pnew_delete(void *p) { Pool *hp = Pool::find_pool(p); // note: if p == 0, then Pool::find_pool(p) will return 0. if (hp) { hp->deallocate(p); } } // elsewhere... class Obj { public: // misc ctors, dtors, etc. // just a sampling of new/del operators void *operator new(size_t s) { return pnew_new(s); } void *operator new(size_t s, Pool *hp) { return pnew_new(s, hp); } void operator delete(void *dp) { pnew_delete(dp); } void operator delete(void *dp, Pool*) { pnew_delete(dp); } void *operator new[](size_t s) { return pnew_new(s); } void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); } void operator delete[](void *dp) { pnew_delete(dp); } void operator delete[](void *dp, Pool*) { pnew_delete(dp); } }; // elsewhere... ClusterPool *cp = new ClusterPool(arg1, arg2, ...); Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);
现在你可以把对象集中在一个单独的内存区域中,通过select池并将它作为一个parameter passing给一个对象的放置,select一个速度非常快但不释放的分配器,使用内存映射以及任何其他想要施加的语义新运营商。
如果你想从初始化分离分配是有用的。 STL使用placement new来创build容器元素。
我用它来通过alloca()构造在堆栈上分配的对象。
无耻的插件:我在这里博客了。
我用它来实时编程。 我们通常不希望在系统启动后执行任何dynamic分配(或取消分配),因为不能保证需要多长时间。
我所能做的是预先分配一大块内存(足够大以容纳课程所需的任何数量)。 然后,一旦我在运行时弄清楚如何构build这些东西,就可以使用placement new来构build我想要的对象。 我知道我使用的一种情况是帮助创build一个异构的循环缓冲区 。
这当然不是狡猾的心,但这就是为什么他们的语法有点粗糙。
头怪:宾果! 你完全了解 – 这正是它的理想之处。 在许多embedded式环境中,外部约束和/或总体使用场景迫使程序员将对象的分配与其初始化分开。 总之,C ++把这个称为“实例化”。 但是无论何时必须显式调用构造函数的动作而没有dynamic或自动分配,放置新元素都是实现这一点的方法。 这也是find固定在硬件组件(内存映射I / O)地址上的全局C ++对象,或者任何静态对象,无论出于什么原因都必须驻留在固定地址的最佳方法。
我用它来创build一个Variant类(即一个对象,可以表示一个单一的值,可以是一个不同的types)。
如果Variant类支持的所有值types都是PODtypes(例如int,float,double,bool),那么带标签的C样式联合就足够了,但是如果您希望某些值types是C ++对象例如std :: string),C unionfunction将不会执行,因为非POD数据types可能不会被声明为联合的一部分。
所以相反,我分配了一个足够大的字节数组(例如sizeof(the_largest_data_type_I_support)),并使用placement new来初始化该区域中适当的C ++对象,当Variant被设置为保持该types的值时。 (当然,当从不同的非POD数据types切换时,事先进行布局删除)
当您要重新初始化全局或静态分配的结构时,这也很有用。
旧的C方法是使用memset()
将所有元素设置为0.由于vtable和自定义对象构造函数,你不能在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. }
序列化时(比如用boost :: serialization),放置new也是非常有用的。 在10年的c ++中,这只是第二种情况,我需要安置新的(第三,如果你包括采访:))。
如果你正在构build一个内核,那么它是很有用的 – 你从哪里放置从磁盘或页表读取的内核代码? 你需要知道在哪里跳转。
或者在其他非常罕见的情况下,比如当你有大量的分配空间,并且想要放置一些结构在另一个之后。 它们可以用这种方式打包,而不需要offsetof()运算符。 但也有其他的技巧。
我也相信一些STL实现使用了新的位置,比如std :: vector。 他们以这种方式为2 ^ n个元素分配空间,并且不需要总是重新分配。
我用它来创build基于包含从networking接收的消息的内存的对象。
我已经看到它被用作“dynamictypes”指针的轻微性能攻击 (在“胡德”一节中):
但是,这是我用来获得快速性能的小技巧:如果被保存的值可以放在void *内部,我实际上并不打扰分配一个新对象,而是使用放置new来强制它进入指针本身。
它被std::vector<>
因为std::vector<>
通常比vector<>
中的objects
分配更多的内存。
我用它来存储内存映射文件的对象。
具体的例子是一个图像数据库,处理大量的大图像(超过内存容量)。
一般来说,贴现新用来摆脱“正常新人”的分配成本。
我使用的另一个场景是我想访问指向还有待构build的对象的指针 ,以实现每个文档的单例。
我已经运行的一个地方是在容器分配一个连续的缓冲区,然后根据需要填充对象。 如前所述,std :: vector可能会这样做,我知道一些版本的MFC CArray和/或CList做到了这一点(因为这是我第一次碰到它的地方)。 缓冲区溢出分配方法是一个非常有用的优化,而放置新的方法几乎是在这种情况下构build对象的唯一方法。 它也有时用于构build分配在直接代码之外的内存块中的对象。
我曾经以类似的身份使用它,虽然它不经常出现。 不过,这是C ++工具箱的一个有用的工具。
脚本引擎可以在本地接口中使用它来从脚本分配本地对象。 有关示例,请参阅Angelscript(www.angelcode.com/angelscript)。
使用共享内存时可能会很方便,除此之外…例如: http : //www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions。 conditions_anonymous_example
我认为这个问题没有被任何答案强调,但新的放置的另一个很好的例子和用法是减less内存碎片(通过使用内存池)。 这在embedded式和高可用性系统中特别有用。 在这最后的情况下,这是特别重要的,因为对于必须运行24/365天的系统来说,没有碎片是非常重要的。 这个问题与内存泄漏无关。
即使使用了一个非常好的malloc实现(或类似的内存pipe理function),很难处理碎片很长一段时间。 在某些时候,如果你不巧妙地pipe理内存预留/释放调用,最终可能会产生许多难以重用的小间隙 (分配给新的预留)。 因此,在这种情况下使用的解决scheme之一是使用内存池来为应用程序对象分配内存。 每次您需要内存某个对象后,您只需使用新的位置在已保留的内存上创build一个新对象。
这样,一旦你的应用程序启动,你已经保留了所有需要的内存。 所有新的内存预留/释放到分配池(您可能有多个池,每个不同的对象类一个)。 在这种情况下,不会发生内存碎片,因为不会出现间隙,而且您的系统可以运行很长时间(几年),而不会出现碎片。
我特别在VxWorks RTOS的实践中看到了这一点,因为它的默认内存分配系统遭受了很多碎片化。 所以通过标准的new / malloc方法分配内存在项目中基本上是被禁止的。 所有的内存预留应该去专用的内存池。
请参阅http://xll.codeplex.com上的xll项目中的fp.h文件。它为那些喜欢随身携带它们的维度的数组解决了“编译器的毫无保证的拙劣”问题。;
typedef struct _FP { 无符号短整型行; 无符号短整型列; 双数组[1]; / *实际上,数组[行] [列] * / } FP;