5

我有一个关于安置的问题new我对C++ 中以下两个代码片段在功能上是否等效并且可以互换使用(我并不是暗示应该使用第二个,当第一个适合时)?

#1

T* myObj = new T();
// Do something with myObj
delete myObj;

#2

char* mem = new char[sizeof(T)];
T* myObj = new (mem) T();
// Do something with myObj
myObj->~T();
delete[] mem;

当我使用像这样的放置新语法时,有什么我应该特别注意的吗?

4

5 回答 5

11

它们不是等效的,因为如果T抛出的构造函数或析构函数它们具有不同的行为。

new T()将释放任何已分配的内存,然后让异常进一步传播。char* mem = new char[sizeof(T)]; T* myObj = new (mem) T();不会(除非你明确地做一些事情来确保它被释放,否则你会有泄漏)。同样,delete myObj将始终释放内存,无论是否~T()抛出。

一个精确的等价物T* myObj = new T();/*other code*/delete myObj;是这样的:

//When using new/delete, T::operator new/delete
//will be used if it exists.
//I don't know how do emulate this in
//a generic way, so this code just uses
//the global versions of operator new and delete.
void *mem = ::operator new(sizeof(T));
T* myObj;
try {
    myObj = new (mem) T();
}
catch(...) {
    ::operator delete(mem);
    throw;
}
/*other code*/
try {
    myObj->~T();
    ::operator delete(mem);
}
catch(...) {
    //yes there are a lot of duplicate ::operator deletes
    //This is what I get for not using RAII ):
    ::operator delete(mem);
    throw;
}
于 2012-05-14T14:59:14.940 回答
8

由于您正在分配原始内存,因此更接近的等价物是:

void *mem = operator new(sizeof(T));
T *myobj = new(mem) T();

// ...

myobj->~T();
operator delete(mem);

请注意,如果您::operator new为特定类重载,这将使用该类' operator new,而您的使用new char []将忽略它。

编辑:虽然我应该补充一点,我在这里忽略了例外的可能性。@Mankarse 的回答似乎(对我来说)很好地涵盖了这部分。

于 2012-05-14T14:58:21.087 回答
0

从根本上讲,您在两种情况下都在做同样的事情:即您在堆上实例化一个新对象并释放内存,但在第二种情况下,您(显然)使用放置 new操作员。

在这种情况下,如果构造函数按照 Mankarse 的解释抛出,则它们都会产生相同的结果,减去差异。

此外,我不会说您可以互换使用它们,因为第二种情况不是如何placement new使用的实际示例。在内存池的上下文中使用可能会更有意义placement new,如果您正在编写自己的内存管理器,以便您可以T在每个分配的内存上放置多个对象(在我的测试中,它往往会节省大约 25% CPU 时间)。如果您有一个更现实的用例,placement new那么您应该担心的事情会更多。

于 2012-05-14T15:00:57.777 回答
0

好吧,您正在为您的 T 对象预分配内存。它应该没问题。然而,如果您将再次重用分配的区域,那是有道理的。否则会比较慢。

于 2012-05-14T15:01:15.453 回答
0

是的。您的示例太简单了,无法演示这一点,但是您预先分配的内存“mem”应该管理存储在“myObj”中的对象。

也许换一种更好的方式,场景#1在堆上分配空间,并在该空间中构造一个对象。场景#2在堆上分配空间,“mem”,然后在该空间的某处构造一个对象。

现在,将第二个对象放在“mem”中的其他位置。它变得复杂,对吧?

myObj 的构造和销毁在两种情况下都是相同的(除非您的构造函数抛出异常,正如 Mankarse 指出的那样),但是分配器在场景 #1 中为您处理内存管理,而不是在场景 #2 中。

因此,请注意适当地管理“mem”。一种常见的方法如下:

template<class T> void destroy(T* p, Arena& a)
{
        if (p) {
                p->~T();        // explicit destructor call
                a.deallocate(p);
        }
}
于 2012-05-14T15:02:12.230 回答