在 C++ 中的预定位置构造对象有什么用?
以下代码说明了在预定位置的构造-
void *address = (void *) 0xBAADCAFE ;
MyClass *ptr = new (address) MyClass (/*arguments to constructor*/) ;
这最终在预定的“地址”处创建了 MyClass 的对象。(假设地址指向的存储空间足够大,可以容纳 MyClass 对象)。
我想知道在内存中这些预定位置创建对象的用途。
在 C++ 中的预定位置构造对象有什么用?
以下代码说明了在预定位置的构造-
void *address = (void *) 0xBAADCAFE ;
MyClass *ptr = new (address) MyClass (/*arguments to constructor*/) ;
这最终在预定的“地址”处创建了 MyClass 的对象。(假设地址指向的存储空间足够大,可以容纳 MyClass 对象)。
我想知道在内存中这些预定位置创建对象的用途。
主要有两种情况:
第一个是当——例如在嵌入式系统中——你必须在给定的众所周知的地方构造一个对象。
第二个是当您出于某种原因想要以默认方式以外的方式管理内存时。
在 C++ 中,表达式 likepA = new(...) A(...)
做了两个连续的事情:
void* operator new(size_t, ...)
函数,然后A::A(...)
。由于调用 new 是调用 A::A() 的唯一方法,因此向 new 添加参数允许专门化不同的方式来管理内存。最琐碎的是“使用已经通过其他方式获得的内存”。
当需要将分配和构建分开时,这种方法很好。典型的情况是std::allocator
,其目的是为给定数量分配未初始化的内存,而对象构造稍后发生。
例如,这种情况发生在 in std::vector
,因为它必须分配capacity
通常比实际更宽的size
,然后push_back
在已经存在的空间中构造对象,因为它们是 -ed 。
事实上,当要求分配 n 个对象时,默认的 std::allocator 实现会执行 a 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 有用的一种情况是:
您可以预先分配大缓冲区一次,然后使用许多放置new
运算符。
这为您提供了更好的性能(您不需要每次都重新分配)和更少的碎片内存(当您需要小内存块时)。通常这是std::vector
实施使用的。
缺点是,您必须手动管理分配的内存。new
一旦不再需要, 按放置分配的对象需要显式调用析构函数。
鉴于始终建议对您的应用程序进行瓶颈分析,而不是跑到放置新位置进行预优化。
通常您在嵌入式或驱动程序代码中使用预定位置,其中某些硬件通过某些地址范围寻址。
但是在这种情况下,该地址的存储不用于访问,或者更好的是它不是预期的(或者更好的是不必用于它,因为您不知道新的运营商正在使用它),稍后在新的执行操作。
您将它用作初始化值(新的并没有真正改变它)。我想到了两个目的:首先,如果您后来忘记了新的,您会立即在调试器中看到您的魔术地址(即在本例中为 0xBAAADCAFE)。
其次,您可以在您摆弄 new 运算符并需要一个 init 值的情况下使用,这样您就可以调试它(例如,您可以看到更改)。
或者你已经修改了你的新操作符,它可以用那个幻数来做任何事情(例如,你可以用它来调试,或者像上面提到的那样,确实为某些硬件使用特定地址的内存),在不同的分配方法之间切换, ...
编辑:要在这种情况下正确回答,需要查看 new 运算符的实际作用,您应该检查该新闻源代码。
当您通过将 long、DWORD、DWORD_PTR 或其他大小的指针作为参数传递给函数并需要重建类的副本以供 OO 使用时,此特定行为非常有用。
或者,这也可以用于在预先分配的内存或您确定为静态的位置中创建一个类(即:您正在将您的应用程序与一些古老的 ASM 库链接)。
自定义分配器、实时(此处没有锁定)和性能。