放置新行为等效

时间:2012-05-14 14:52:41

标签: c++ memory-management new-operator placement-new

我对C ++中的new语法位置有疑问。以下两个代码片段在功能上是否相同,并且可以互换使用(我并不是说第二个应该使用,当第一个适合时)?

#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;

当我使用像这样的放置新语法时,我应该特别小心吗?

5 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:8)

由于你正在分配原始内存,因此更接近的是:

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

// ...

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

请注意,如果您为某个特定班级重载::operator new,则会使用该班级' operator new,您使用new char []的地方会忽略它。

编辑:虽然我应该补充一点,我忽略了例外的可能性。 @ Mankarse的答案似乎(对我而言)能够很好地覆盖这一部分。

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

或许采用更好的方法,方案#1 在堆上分配空间,并在该空间中构造一个对象。场景#2 在堆上分配空间“mem”,然后在该空间内构建对象某处

现在,在“mem”中将第二个对象放在其他地方。它变得复杂,对吗?

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

因此,请注意妥善管理“mem”。一个common approach如下:

template<class T> void destroy(T* p, Arena& a)
{
        if (p) {
                p->~T();        // explicit destructor call
                a.deallocate(p);
        }
}
相关问题