Estoy confundido sobre el estado de un objeto después de que se ha movido utilizando la semántica de movimiento C++ 0x. Según tengo entendido, una vez que un objeto se ha movido, sigue siendo un objeto válido, pero su estado interno ha sido alterado de modo que cuando se llama a su destructor, no se desasignan recursos.Objetos de Zombie después de std :: mover
Pero si mi comprensión es correcta, el destructor de un objeto movido debe llamarse.
embargo, que no ocurre cuando realizo una prueba sencilla:
struct Foo
{
Foo()
{
s = new char[100];
cout << "Constructor called!" << endl;
}
Foo(Foo&& f)
{
s = f.s;
f.s = 0;
}
~Foo()
{
cout << "Destructor called!" << endl;
delete[] s; // okay if s is NULL
}
void dosomething() { cout << "Doing something..." << endl; }
char* s;
};
void work(Foo&& f2)
{
f2.dosomething();
}
int main()
{
Foo f1;
work(std::move(f1));
}
Esta salida:
Constructor called!
Doing something...
Destructor called!
Aviso al destructor sólo se le llama una vez. Esto muestra que mi comprensión aquí está apagada. ¿Por qué no se llamó al destructor dos veces? Aquí está mi interpretación de lo que debería han sucedido:
Foo f1
se construye.Foo f1
se pasa awork
, que toma un rvaluef2
.- El constructor movimiento de
Foo
es llamada, moviendo todos los recursos enf1
af2
. - Ahora se llama al destructor
f2
, liberando todos los recursos. - ahora
f1
's destructor se llama, que en realidad no hace nada ya que todos los recursos fueron transferidos af2
. Aún así, el destructor es llamado, no obstante.
Pero como solo se llama a un destructor, el paso 4 o el paso 5 no están sucediendo. Hice una traza inversa desde el destructor para ver desde dónde se estaba invocando, y se está invocando desde el paso 5. Entonces, ¿por qué no se llama también al destructor f2
?
EDITAR: Bien, modifiqué esto por lo que en realidad está administrando un recurso. (Un búfer de memoria interna.) Aún así, obtengo el mismo comportamiento donde el destructor solo se llama una vez.
¿en qué compilador está probando? – jalf
gcc 4.3 ......................... – Channel72
En ese caso, vale la pena señalar que está escrito en contra de una versión mucho más antigua de C++ 0x borrador. Y la semántica de movimiento ha cambiado bastante desde entonces. Por ejemplo, creo que un compilador más nuevo habría rechazado su código por completo antes de agregar el 'std :: mover'. No sería capaz de llamar a 'work' porque el argumento era efectivamente una referencia lvalue porque se llamaba. – jalf