Estoy creando Unit Test para el código que fue escrito por otra persona, y no se pueden modificar.

Estoy tratando de destruir un objeto y luego instanciarlo desde el principio (para que el constructor lo ponga en la condición inicial)

Este es mi código:

state::State_Machine state_machine_test_off; 

//Check that OFF is initial state

EXPECT_EQ(States::OFF,state_machine_test_off.get_state());

//Change and check the behaviour from OFF to the other States

state_machine_test_off.change_state(States::LOADED);
EXPECT_EQ(States::LOADED,state_machine_test_off.get_state());
state_machine_test_off.~State_Machine(); //Destructor to iniciate again the process

state::State_Machine state_machine_test_off;
state_machine_test_off.change_state(States::INITIALISED);
EXPECT_NE(States::INITIALISED,state_machine_test_off.get_state());
state_machine_test_off.~State_Machine(); //Destructor to iniciate again the process

Y luego continuar. La razón por la que no quiero crear una instancia de un nuevo objeto es porque tengo una gran matriz de estados, y cada uno de ellos puede moverse a través de change_state () a algunos pero no a otros.

He buscado en Internet sobre Destructores, y leí que pueden llamarse explícitamente, incluso si no es una muy buena práctica.

¿Quizás alguien podría aclararme por qué falla el compilador cuando destruyo y luego instanciar nuevamente el objeto?

PD: el destructor es un destructor predeterminado

state::State_Machine::~State_Machine() {}
-2
Alejandro Martinez 30 abr. 2020 a las 19:32

2 respuestas

La mejor respuesta

No puede simplemente declarar otra variable con el mismo nombre en el mismo nivel de alcance, incluso después de llamar a un destructor. El destructor no elimina el nombre antiguo del ámbito.

Para resolver su problema, le sugiero que agregue nuevos niveles de alcance, ya que parece que solo está utilizando su state::State_Machine para algunas líneas en un momento dado.

{ state::State_Machine state_machine_test_off;
  state_machine_test_off.change_state(States::LOADED);
  EXPECT_EQ(States::LOADED,state_machine_test_off.get_state()); }

{  state::State_Machine state_machine_test_off;
   state_machine_test_off.change_state(States::INITIALISED);
   EXPECT_NE(States::INITIALISED,state_machine_test_off.get_state()); }

Si aún desea invocar explícitamente el destructor, debe asegurarse de que sea seguro que se llame al destructor varias veces en el mismo objeto (dijo que no puede modificar el código existente, por lo que esto funcionará o no) . En la práctica, me sorprendería si esto funciona bien, ya que la mayoría de los destructores suponen que el objeto va a ser destruido, y no se tomarán el tiempo para marcarlo como destruido (esto puede requerir un estado adicional, y definitivamente requeriría ciclos) .

Si todavía está determinado, a pesar de la complejidad y los riesgos, podría escribir algo como esto:

state_machine_test_off.~State_Machine(); //Destructor to iniciate again the process

new (&state_machine_test_off) state::State_Machine;

Esto aprovecha la ubicación new, algo que es completamente innecesario si solo usa el alcance correctamente (ver arriba). También debe tener en cuenta lo que sucede si el constructor falla, especialmente porque todavía necesitará manejar el objeto ya desrutado con su destructor llamado nuevamente.

0
Stephen Newell 30 abr. 2020 a las 16:46

El problema es que está redefiniendo una variable que ya ha definido. Cuando tu lo hagas

state::State_Machine state_machine_test_off; 

Básicamente, bloquea el nombre state_machine_test_off en el ámbito para que sea esa variable. Más tarde, no puede hacer state::State_Machine state_machine_test_off; para restablecerlo porque eso intenta definir una nueva variable y el nombre state_machine_test_off ya se ha tomado.

Para reconstruir el objeto después de destruirlo, debe usar colocación nueva para que vuelva a llamar al constructor en ese objeto. Eso se vería como

new (&state_machine_test_off) state::State_Machine();

Y ahora hay una nueva construcción predeterminada state::State_Machine en el almacenamiento de state_machine_test_off.


Dicho esto, todas estas llamadas manuales y las posibilidades de comportamiento indefinido desaparecen si solo reasigna el objeto a un valor predeterminado construido como

state_machine_test_off = state::State_Machine{};
1
NathanOliver 30 abr. 2020 a las 16:46