2012-05-14 3 views
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; 

Есть ли что-то я должен быть особенно осторожным, когда я использую размещения нового синтаксиса, как это?

ответ

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; 
} 
+0

Будет ли правильно выделена память, выделенная 'new char [sizeof (T)]'? –

+3

@TadeuszKopec: стандарт гарантирует максимально возможное выравнивание памяти. Это выполняется только в том случае, если вы используете естественные выравнивания (т. Е. Появляетесь со встроенными типами), если вы используете прагмы, чтобы выровнять выравнивание типа, скажем, вдвое больше, чем все, то все ставки отключены. –

8

Поскольку вы выделение сырой памяти, пристальный эквивалент будет:

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

// ... 

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

Обратите внимание, что если вы перегружены ::operator new для конкретного класса, это будет использовать этот класс operator new, где твои, используя new char [] будет игнорировать его.

Редактировать: хотя я должен добавить, что я игнорирую возможность исключений здесь. @ Ответ Манкарсе кажется (мне), чтобы покрыть эту часть довольно хорошо.

0

На фундаментальном уровне, вы делаете то же самое в обеих ситуациях: то есть вы инстанцировании новый объект в куче и вам 'освобождение памяти, но во втором случае вы (очевидно) используете новый оператор размещения.

В этом случае оба они дали бы те же результаты, за вычетом различий, если конструктор выбрасывает, как объяснил Манкарсе.

Кроме того, я бы не сказал, что вы можете использовать их взаимозаменяемо, потому что второй случай не является реалистичным примером того, как используется placement new. Вероятно, было бы гораздо разумнее использовать placement new в контексте пула памяти, и если вы пишете свой собственный менеджер памяти, чтобы вы могли разместить несколько объектов T на выделенной памяти (в моих тестах он имеет тенденцию экономить 25% процессорного времени). Если у вас есть более реалистичный вариант использования для placement new, тогда будет намного больше вещей, о которых вам следует беспокоиться.

0

Ну, вы предварительно выделяете память для своего объекта T. И все должно быть хорошо. Тем не менее, имеет смысл, если вы повторно используете выделенную область еще раз. В противном случае он будет медленнее.

0

Да. Ваш пример слишком прост, чтобы продемонстрировать это, но память, которую вы выделили заранее, «mem», должна управлять объектом, хранящимся внутри, «myObj."

Возможно поставить лучший путь, сценарий # 1 выделяет место в куче, и создает объект в этом пространстве. Сценарий # 2 выделяет место в куче,„мем“, а затем создает объект где в пределах этого пространства.

Теперь, поместите второй объект где-то еще в «мем». Это становится сложным, верно?

строительство и разрушение myObj происходят одинаково в обоих сценариях (за исключением c ase вашего конструктора, бросающего исключение, как указывает 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); 
     } 
}