这里有人用过c++的“placement new”吗?如果有,为什么?在我看来,它只在内存映射硬件上有用。


当前回答

Head Geek: BINGO! You got it totally - that's exactly what it's perfect for. In many embedded environments, external constraints and/or the overall use scenario forces the programmer to separate the allocation of an object from its initialization. Lumped together, C++ calls this "instantiation"; but whenever the constructor's action must be explicitly invoked WITHOUT dynamic or automatic allocation, placement new is the way to do it. It's also the perfect way to locate a global C++ object that is pinned to the address of a hardware component (memory-mapped I/O), or for any static object that, for whatever reason, must reside at a fixed address.

其他回答

放置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的常见问题:是否存在“位置删除”?

我们将它用于自定义内存池。简单介绍一下:

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool *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, ...);

现在你可以将对象聚集在一个单独的内存区域中,选择一个非常快但不进行释放的分配器,使用内存映射,以及任何你希望通过选择池并将其作为参数传递给对象的放置new操作符来施加的语义。

在序列化时(比如使用boost::serialization),放置new也非常有用。在c++的10年里,这只是我第二次需要新职位的情况(如果你包括面试的话,这是第三次:))。

它可能在使用共享内存时很方便,在其他用途中…例如:http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions.conditions_anonymous_example

如果你想把分配和初始化分开,这是很有用的。STL使用放置new来创建容器元素。