用途:在C++中的预定位置构造对象

Use of : Construction of objects at predetermined location in C++

本文关键字:位置 定位 对象 C++ 用途      更新时间:2023-10-16

在C++中的预定位置构造对象有什么用?

以下代码说明了在预定位置的施工-

void *address = (void *) 0xBAADCAFE ;
MyClass *ptr = new (address) MyClass (/*arguments to constructor*/) ;

这最终会在预定的"地址"创建MyClass的对象。(假设地址指向的存储空间足够大,可以容纳MyClass对象)。

我想知道在内存中这样预先确定的位置创建对象的用途。

一个新放置很有用的场景是:

您可以预先分配一次大缓冲区,然后使用多个放置new运算符
这会给你更好的性能(你不需要每次都重新分配)和更少的碎片内存(当你需要小内存块时)。这通常是std::vector实现所使用的。

缺点是,您必须手动管理分配的内存。由放置new分配的对象一旦不再需要,就需要显式析构函数调用。

考虑到为瓶颈分析您的应用程序始终是可取的,而不是跑到新的位置进行预优化。

主要有两种情况:

第一种情况是,例如在嵌入式系统中,必须在给定的已知位置构造对象。

第二种情况是,出于某种原因,您希望以默认以外的方式管理内存。

在C++中,像pA = new(...) A(...)这样的表达式做两件连续的事情:

  1. 调用void* operator new(size_t, ...)函数,然后
  2. 调用CCD_ 6

由于调用new是调用A::A()的唯一方法,因此向new添加参数可以专门化不同的内存管理方式。最琐碎的是"使用通过其他方式获得的内存"。

当分配和施工需要分开时,这种方法很好。典型的情况是std::allocator,其目的是为给定的数量分配未初始化的内存,而对象构造发生在稍后。

例如,在std::vector中会发生这种情况,因为它必须分配一个通常比实际size宽的capacity,然后在已经存在的空间中构造对象作为push_back

事实上,默认的std::allocater实现,当asket分配n个对象时,会执行return reinterpret_cast<T*>(new char[n*sizeof(T)]),因此分配空间,但实际上不构造任何内容。

承认std::vector存储:

T* pT;  //the actual buffer
size_t sz; //the actual size
size_t cap; //the actual capacity
allocator<T> alloc;

push_back的实现可以是:

void vector<T>::push_back(const T& t)
{
   if(sz==cap)
   {
       size_t ncap = cap + 1+ cap/2; //just something more than cap 
       T* npT = alloc.allocate(ncap); 
       for(size_t i=0; i<sz; ++i) 
       {
          new(npT+i)T(pt[i]); //copy old values (may be move in C++11)
          pt[i].~T(); // destroy old value, without deallocating
       }
       alloc.deallocate(pt,cap);
       pT = npT; 
       cap = ncap;
       // now we heve extra capacity       
   }
   new(pT+sz)T(t); //copy the new value
   ++sz; //actual size grown
}

本质上,需要将分配(与整个缓冲区相关)与元素的构建(必须在现有缓冲区中进行)分开。

通常在嵌入式或驱动程序代码中使用预先确定的位置,其中一些硬件通过特定的地址范围进行寻址。

但在这种情况下,该地址的存储不用于访问,或者更好的是,它不是有意的(或者最好不必用于访问,因为你不知道新操作员正在使用它),因为稍后会执行新操作。

您使用它作为初始化值(new并没有真正更改它)。我想到了两个目的:首先,如果你后来忘记了一个新的,你会立即在调试器中看到你的魔法地址(即在本例中为0xBAAADCAFE)。

其次,您可以在摆弄新操作符并需要init值的情况下使用,这样您就可以调试它(例如,您可以看到更改)。

或者你已经修改了你的新操作符,它用这个神奇的数字做任何事情(例如,你可以用它进行调试,或者,像上面提到的那样,真正使用特定地址的内存用于某些硬件),在不同的分配方法之间切换。。。

EDIT:在这种情况下,要想回答正确,需要查看新操作员的实际操作,您应该检查新闻源代码。

当您通过将一个长的DWORD、DWORD_PTR或其他大小的指针作为参数传递给函数来知道类的地址,并且需要重建该类的副本以供O-O使用时,这种特殊行为非常有用。

或者,这也可以用于在预先分配的内存或您确定为静态的位置中创建类(即:您正在将应用程序与一些古老的ASM库链接)。

自定义分配器、实时(此处无锁)和性能。