2012-05-14 8 views
5

Tengo una pregunta con respecto a la sintaxis de la ubicación new en C++. ¿Los dos fragmentos de código siguientes son funcionalmente equivalentes y se pueden usar indistintamente (no estoy implicando que se debe usar el segundo, cuando el primero es adecuado)?Colocación nuevo comportamiento equivalente

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

¿Hay algo que debería ser especialmente cuidadoso de, cuando estoy usando la colocación de una nueva sintaxis de esta manera?

Respuesta

11

No son equivalentes, porque tienen un comportamiento diferente si el constructor o el destructor de T arroja.

new T() liberará la memoria que se haya asignado antes de permitir que la excepción se propague más. char* mem = new char[sizeof(T)]; T* myObj = new (mem) T(); no (y a menos que explícitamente haga algo para asegurarse de que se libere, tendrá una fuga). Del mismo modo, delete myObj siempre desasignará la memoria, independientemente de si arroja ~T().

un equivalente exacto de T* myObj = new T();/*other code*/delete myObj; sería algo así como:

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

¿La memoria asignada por 'new char [sizeof (T)]' se alineará correctamente? –

+3

@TadeuszKopec: el estándar garantiza que la memoria tenga la alineación máxima posible. Esto solo se aplica si usa alineaciones naturales (es decir, que aparecen con los tipos incorporados), si usa pragmas para ajustar la alineación de un tipo a, digamos, dos veces ese máximo, entonces todas las apuestas estarán desactivadas. –

8

Puesto que usted está asignando memoria prima, un equivalente más cercano sería:

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

// ... 

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

Tenga en cuenta que si ha sobrecargado ::operator new para una clase en particular, para ello se utiliza esa clase operator new, donde el suyo usando new char [] lo ignoraría.

Editar: aunque debería agregar que estoy ignorando la posibilidad de excepciones aquí. La respuesta de @Mankarse parece (para mí) cubrir bastante bien esa parte.

0

En un nivel fundamental, que está haciendo lo mismo en ambas situaciones: es decir, que está instanciar un nuevo objeto en el montón y te Está liberando la memoria, pero en el segundo caso está (obviamente) usando el nuevo operador de colocación.

En este caso, ambos darían los mismos resultados, menos las diferencias si el constructor lanza como explicó Mankarse.

Además, no diría que puede usarlos indistintamente, porque el segundo caso no es un ejemplo realista de cómo se usa placement new. Probablemente tenga mucho más sentido usar placement new en el contexto de un grupo de memoria y si está escribiendo su propio administrador de memoria para poder colocar múltiples objetos T en la memoria asignada (en mis pruebas tiende a guardarse aproximadamente). 25% de tiempo de CPU). Si tiene un caso de uso más realista para placement new, entonces habrá muchas más cosas de las que debería preocuparse.

0

Bueno, usted está asignando memoria para su objeto T. Y debería estar bien. Sin embargo, tiene sentido si reutiliza el área asignada una vez más. De lo contrario, será más lento.

0

Sí. Su ejemplo es demasiado simple para demostrar esto, pero la memoria que asignó de antemano, "mem", debe administrar el objeto almacenado dentro de "myObj"."

Tal vez puso una mejor manera, escenario # 1 asigna espacio en el montón, y construye un objeto en ese espacio. Escenario # 2 asigna espacio en el montón, 'mem', entonces construye un objeto en algún lugar dentro de ese espacio.

Ahora, poner un segundo objeto demás algún lugar dentro de "mem." se vuelve complicado, ¿verdad?

La construcción y destrucción de myObj están sucediendo de forma idéntica en ambos escenarios (excepto en la C de su constructor lanzando una excepción, como señala Mankarse), pero el asignador se ocupa de su gestión de memoria en el escenario n. ° 1 y no en el escenario n. ° 2.

Por lo tanto, tenga cuidado de administrar "mem" apropiadamente. Uno common approach es el siguiente:

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