2010-01-29 21 views
45

¿es posible reinicializar un objeto de una clase utilizando su constructor?Llamar a un constructor para reinicializar el objeto

+1

¿Qué quiere decir exactamente con reiniciar? – David

+0

Dando a las variables los mismos valores cuando se crearon por primera vez. – cpx

+0

La solución de Neil es "lo mejor", aunque su diseño necesita reparación. – GManNickG

Respuesta

56

o menos. Dada una clase A:

A a; 
... 
a = A(); 

la última afirmación no es de inicialización, es la asignación, pero probablemente hace lo que quiere.

+9

+1 Por supuesto, esto también requiere escribir un operador de asignación completo y correcto. –

+15

@Greg Esto requiere TENER un operador de asignación correcto, no necesariamente ESCRIBIR uno: el valor predeterminado a menudo será perfectamente correcto. –

+0

¿Causará esto una pérdida de recursos si 'a' se instanciara usando una palabra clave' new'? – davidhood2

10

No, los constructores solo se llaman cuando se crea el objeto por primera vez. Escribe un nuevo método para hacerlo en su lugar.

Editar

no voy a reconocer la colocación de nuevo, porque no quiero tener que conseguir un raptor mascota para el trabajo.

See this comic, pero pensar en el tema a la mano ...

9

Respuesta corta:

No. Si parte del comportamiento previsto de su objeto debe ser inicializado varias veces, entonces la mejor manera de implementar esto es a través de un método de inicialización accesible. El constructor de tu clase simplemente puede diferir a este método. esquina

class C1 { 
public: 
    C1(int p1, int p2) { 
    Init(p1,p2); 
    } 
    void Init(int p1, int p2) { ... } 
}; 

nitpicker:

¿Hay alguna manera increíblemente mal para llamar a un constructor en C++ después de un objeto se crea? Casi con certeza, esto es C++ después de todo. Pero es fundamentalmente malo y su comportamiento casi con seguridad no está definido por el estándar y debe evitarse.

+2

Podría hacer: 'T x; x-> ~ T(); new (& x) T(); 'No estoy seguro de lo definido que es, casi parece estar bien. (Ok como legal, no está bien ya que en este código es totalmente increíble.) – GManNickG

+4

@GMan, me estremecí visiblemente cuando leí eso :) – JaredPar

27

Es posible, aunque es una muy mala idea. La razón por la cual es que sin llamar a los destructores en el objeto existente, va a perder recursos.

Con esa advertencia importante, si insiste en hacerlo, puede utilizar la ubicación nueva.

// Construct the class 
CLASS cl(args); 

// And reconstruct it... 
new (&cl) CLASS(args); 
+5

Deberías obtener un -1 solo por sugerirle a alguien: P, pero un +1 para el truco - no lo sabía, entonces 0 en total –

+7

Uno podría 'cl-> ~ CLASE();' antes de la colocación nueva, como dije en la respuesta de Jared. No estoy seguro si eso está definido, parece legal. – GManNickG

+2

+1, única solución correcta. Nada malo, es la forma en que se hace. –

38

¿Literalmente? Sí, al usar la colocación nueva. Pero primero debes destruir el objeto previamente construido.

SomeClass object(1, 2, 3); 
... 
object.~SomeClass(); // destruct 
new(&object) SomeClass(4, 5, 6); // reconstruct 
... 
// Final destruction will be done implicitly 

Sin embargo, el valor de esto no va más allá de lo puramente teórico. No lo hagas en la práctica. Todo es feo más allá de la descripción.

+13

¿Puede explicar por qué cree que esto es "feo más allá de la descripción"? Me parece que tiene un uso legítimo para evitar la duplicación de código y los errores que surgen al tener un método 'claro' separado de un constructor y un destructor –

+3

este "feo" es que después de llamar al destructor en 'object', tiene una variable de pila no inicializada. Intentar hacer cualquier cosa con' object' después de este punto es inseguro. No hay ni siquiera una manera de tect que el objeto ha sido destruido más tarde. Pero una vez reconstruido, es válido de nuevo. Yo diría que siempre y cuando esas dos líneas estén consecutivas, esto es seguro. –

+0

Honestamente, probablemente pondría esto en algún tipo de función de plantilla vardic o incluso solo en una macro, para dejar en claro lo que está sucediendo y para evitar que el código se inserte accidentalmente entre las dos declaraciones. – LivePastTheEnd

1

Puede que no sea lo que tiene en mente, pero como no mencionó para qué sirve, supongo que una respuesta sería que lo haría controlando el alcance y el flujo del programa.

Por ejemplo, no se escribiría un juego como este:

initialize player 
code for level 1 
... 
reinitialize player 
code for level 2 
... 
etc 

En su lugar, habría esfuerzan para:

void play_level(level_number, level_data) { 
    Player player; //gets "re-initialized" at the beginning of each level using constructor 
    //code for level 
} 

void game() { 
    level_number = 1; 
    while (some_condition) { 
     play_level(level_number, level_data); 
     ++level_number; 
    } 
} 

(esquema muy aproximado para transmitir la idea, no pretende ser compilable de forma remota.)

6

Sí, puedes hacer trampa y usar la ubicación nueva.
Nota: No aconsejo esto:

#include <new> 

reInitAnA(A& value) 
{ 
    value.~A();   // destroy the old one first. 
    new (&value) A();  // Call the constructor 
          // uses placement new to construct the new object 
          // in the old values location. 
} 
+0

¿Qué pasa si el nuevo falla? – Jagannath

+1

En este caso, no puede fallar el nuevo (en términos de asignación de memoria) ya que no se asigna memoria. El constructor puede fallar y lanzar excepciones tal como cabría esperar. –

+0

Posiblemente lo que quiere decir Jagannath es, ¿qué ocurre si se lanza una excepción y el objeto pasado como parámetro es, por ejemplo,? una variable automática que pertenece a la persona que llama, o un objeto asignado dinámicamente sostenido por un puntero inteligente. ¿Cuándo es válido llamar al destructor dos veces, cómo se puede escribir código para garantizar la validez, etc.? –

1

En lugar de destruirse y reinicializar según lo sugerido por algunas de las respuestas anteriores, es mejor que hacer una misión, como a continuación. El código a continuación es excepcionalmente seguro.

T& reinitialize(int x, int y) 
    { 
     T other(x, y); 
     Swap(other); // this can't throw. 
     return *this; 
    } 
7

En C++ 11, se puede hacer esto:

#include <type_traits> 

template <class T, typename... Args> 
void Reconstruct(T& x, Args&&... args) 
{ 
    static_assert(!std::has_virtual_destructor<T>::value, "Unsafe"); 
    x.~T(); 
    new (&x) T(std::forward<Args>(args)...); 
} 

Esto le permite utilizar Reconstruct pasan parámetros del constructor arbitrarias a cualquier objeto. Esto puede evitar tener que mantener un montón de métodos Clear, y errores que pueden pasar desapercibidos si en algún momento el objeto cambia, y el método Clear ya no coincide con el constructor.

Lo anterior funcionará bien en la mayoría de los contextos, pero falla horriblemente si la referencia es a una base dentro de un objeto derivado que tiene un destructor virtual. Por esta razón, la implementación anterior impide el uso con objetos que tienen un destructor virtual.

2

que suelo escribir lo siguiente en C moderna ++:

SomeClass a; 
... 
a = decltype(a)(); 

Puede ser no es la forma más eficaz, ya que construye de manera efectiva otro objeto del mismo tipo de a y lo asigna a a, pero funciona en la mayoría de los casos, no tiene que recordar el tipo de a, y se adapta si el tipo cambia.

Cuestiones relacionadas