2010-10-18 15 views
14

¿Cuál es la segunda línea? (Visto mientras que contesta a otra pregunta.)¿Qué es esta segunda novedad?

int * x = new int [1] ; 
int * y = new (x) int; 

Después de la segunda línea de X e Y tienen el mismo valor (punto a un mismo lugar). ¿Cuál es la diferencia entre y = x y la segunda línea? ¿Es como un constructor o algo así?

+0

Nota: La ubicación nueva no suele utilizarse con int (ya que no tiene ninguna ventaja (int no tiene un constructor)). –

+0

@Martin: aunque 'new (x) int()' sería diferente y podría usarse a través de una plantilla. Usar siempre parens, incluso con new [], es una buena idea. –

+0

Un enlace a la otra pregunta donde viste esto podría ser útil. –

Respuesta

13

Es placement new. Construye un nuevo int en la memoria apuntada por x.

Si intenta:

int * x = new int [1]; 
*x = 5; 
std::cout << *x << std::endl; 
int * y = new (x) int; 
*y = 7; 
std::cout << *x << std::endl; 

la salida será:

5 
7 
+0

pero x no cambia donde ya apuntaba –

+0

Nada le sucede a 'x'. Pero algo puede sucederle a '* x'. Intente lo siguiente: 'int * x = new int [1]; * x = 5; std :: cout << * x << std :: endl; int * y = new (x) int; * y = 7; std :: cout << * x << std :: endl; ' –

+0

Código @Green: es correcto. La colocación nueva solo realiza la inicialización. Es como llamar al constructor de ese tipo en la memoria asignada. –

4

Esto se llama placement new. Le permite construir un objeto en la memoria ya asignado.

Este hilo anterior discute where and how it is useful for.

+0

¡No lo entiendo completamente! cuando hacemos algo nuevo, ¡se cambia el puntero! pero ¿cómo funciona esto sin cambiar el puntero? vea la salida de VCEXPRESS8: x antes de la segunda línea 0x005c4e58 x después de que la segunda línea 0x005c4e58 sea la misma! –

+1

Le dice al compilador "hacer el constructor habitual, pero en lugar de crear más memoria, use la memoria en' x'. " Por lo tanto, no modifica 'x', sino que sobrescribe el' int' que creó en la primera línea. –

+0

@Green, no hay memoria nueva asignada con ubicación nueva, esta es la razón por la cual 'y' en su ejemplo apunta a la misma dirección de memoria que' x'. Considérelo simplemente omitir el paso de asignación de memoria y ejecutar inmediatamente la llamada de constructor apropiada utilizando el bloque de memoria suministrado. –

3

La segunda es una nueva "nueva ubicación". Realiza la inicialización (es decir, llama a cualquier constructor necesario) sin realizar ninguna asignación. Es útil cuando necesita crear un esquema de asignación de memoria personalizado.

2
int * y = new (x) int; 

Según la ubicación, nueva sintaxis.

EDITAR: Junto con la asignación personalizada, la colocación nueva también ayuda a a volver a inicializar un estado del objeto como a continuación.

class Test 
{ 
    int startVal; 
public: 
    Test() 
    { 
     startVal = 1; 
    } 
    void setVal(int val) { startVal = val; } 
}; 
int main() 
{ 
    Test *p = new Test; //Creates new object and initializes it with 
          //a call to constructor. 
    p->setVal(10); //Change object content. 
    new(p) Test; //Reset object: 
    //object pointed by p will be re-initialzed here by making 
    //a call to constructor. startVal will be back to 1 
} 

Como se describe en los comentarios anteriores, el restablecimiento del estado del objeto también se puede lograr mediante la colocación nueva. colocación nueva no asigna memoria, construye el objeto en la dirección especificada en la paránthesis.

+0

Eso no es una buena idea. ¿Qué sucede si Test contiene un miembro de puntero? Como no llamó al destructor en el objeto original, probablemente perderá memoria. Si va a utilizar la colocación nueva para restablecer un valor (que generalmente es una mala idea), primero debe llamar al destructor en el objeto (puede llamar manualmente al destructor mediante el puntero). –

+0

@Martin: Buen punto. Pero vi que se usaba el reajuste en nuestro código comercial ampliamente utilizado. No tienen ningún puntero dentro de la clase. No estoy seguro de si es una mala práctica. De todos modos, gracias. – bjskishore123

3

Esta es la ubicación nueva.

Aunque generalmente no lo usa con tipos enteros.
Se usa generalmente para construir un búfer donde luego se crean otros tipos.

// Allocate a buffer with enough room for two T objects. 
char* buffer = new char[sizeof(T) * 2]; 

// Allocate a T in slot zero 
T* t1 = new (buffer + 0 * sizeof(T)) T("Zero"); 

// Allocate a T in slot one 
T* t2 = new (buffer + 1 * sizeof(T)) T("One"); 

Eso es lo básico.
Pero recuerde que los objetos asignados con la ubicación nueva no se pueden eliminar con la instrucción delete. Esto es porque delete intenta reclamar la memoria asignada por new (así como también llamar al destructor). Entonces, para usar estos objetos correctamente, debe llamar manualmente allí al destructor.

t1->~T(); 
t2->~T(); 

No olvides eliminar el búfer original.

delete [] buffer; 

A otros algunas advertencias:
gente a menudo ve ese búfer podría ser implementado en la pila y así ser liberados de forma automática

char buffer[sizeof(T) * 2]; 

Desafortunadamente esto puede ser técnicamente OK (Compila). Pero no se garantiza que funcione ya que la memoria del búfer puede no estar alineada correctamente para que se coloque una T en su interior. Por lo tanto, debe asignar el búfer dinámicamente (mediante el uso de la función nueva garantiza que la memoria está alineada correctamente para cualquier objeto del tamaño asignado (por lo tanto, también se alinea para cualquier tamaño más pequeño que el tamaño asignado). La forma más fácil de moverse este problema es utilizar un std :: vector

std::vector<char> buffer(sizeof(T) * 2); 
T* t1 = new (&buffer[0] + 0 * sizeof(T)) T("Zero"); 
T* t2 = new (&buffer[0] + 1 * sizeof(T)) T("One"); 

Otro uso de la colocación de nuevo es restablecer un objeto
he visto este hecho, pero yo prefiero usar el operador de asignación más estándar:.

T obj1("Plop"); 
obj1 = T("Another Plop"); 

// Can be done like this: 
T obj1("Plop"); 
obj1.~T(); 
new (&obj1) T("Another Plop"); // Seems excessive to me. But can be us-full 
           // in some extreme situations. 

Recuerde que si usa el método de reinicio primero debe destruir el objeto viejo (o el objeto puede no comportarse correctamente).