Question

I have a question regarding placement new syntax in C++. Are the following two code snippets functionally equivalent and can be used interchangeably (I am not implying that the second should be used, when the first one is suitable)?

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

Is there something I should be especially careful of, when I am using the placement new syntax like this?

Était-ce utile?

La solution

They are not equivalent, because they have different behaviour if the constructor or the destructor of T throws.

new T() will free any memory that has been allocated before letting the exception propagate any further. char* mem = new char[sizeof(T)]; T* myObj = new (mem) T(); will not (and unless you explicitly do something to ensure that it gets freed you will have a leak). Similarly, delete myObj will always deallocate memory, regardless of whether ~T() throws.

An exact equivalent for T* myObj = new T();/*other code*/delete myObj; would be something like:

//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;
}

Autres conseils

Since you're allocating raw memory, a closer equivalent would be:

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

// ...

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

Note that if you've overloaded ::operator new for a particular class, this will use that class' operator new, where yours using new char [] would ignore it.

Edit: though I should add that I'm ignoring the possibility of exceptions here. @Mankarse's answer seems (to me) to cover that part fairly well.

On a fundamental level, you're doing the same thing in both situations: i.e. you're instantiating a new object on the heap and you're releasing the memory, but in the second case you're (obviously) using the placement new operator.

In this case both of them would yield the same results, minus the differences if the constructor throws as Mankarse explained.

In addition, I wouldn't say that you can use them interchangeably, because the second case is not a realistic example of how placement new is used. It would probably make a lot more sense to use placement new in the context of a memory pool and if you're writing your own memory manager so you can place multiple T objects on the per-allocated memory (in my tests it tends to save about 25% CPU time). If you have a more realistic use case for placement new then there will be a lot more things that you should worry about.

Well, you are pre-allocating memory for your T object. And it should be fine. Nevertheless it makes sense if you will reuse allocated area one more time. Otherwise It will be slower.

Yes. Your example is too simple to demonstrate this, but the memory you allocated in advance, "mem," should manage the object stored within, "myObj."

Perhaps put a better way, scenario #1 allocates space on the heap, and constructs an object in that space. Scenario #2 allocates space on the heap, "mem," then constructs an object somewhere within that space.

Now, put a second object somewhere else within "mem." It gets complicated, right?

The construction and destruction of myObj are happening identically in both scenarios (except in the case of your constructor throwing an exception, as pointed out by Mankarse), but the allocator is taking care of your memory management for you in scenario #1, and not in scenario #2.

So, be careful of managing "mem" appropriately. One common approach is the following:

template<class T> void destroy(T* p, Arena& a)
{
        if (p) {
                p->~T();        // explicit destructor call
                a.deallocate(p);
        }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top