Pregunta básica: ¿cuándo un programa llama al método destructor de una clase en C ++? Me han dicho que se llama cuando un objeto sale del alcance o se somete a unadelete
Preguntas más específicas:
1) Si el objeto se crea a través de un puntero y ese puntero se elimina más tarde o se le da una nueva dirección a la que apuntar, ¿el objeto al que apuntaba llama a su destructor (asumiendo que nada más lo apunta)?
2) Continuando con la pregunta 1, qué define cuándo un objeto sale del alcance (sin tener en cuenta cuándo un objeto sale de un {bloque} dado). Entonces, en otras palabras, ¿cuándo se llama a un destructor en un objeto en una lista vinculada?
3) ¿Alguna vez querría llamar a un destructor manualmente?
c++
destructor
Pat Murray
fuente
fuente
Respuestas:
Depende del tipo de punteros. Por ejemplo, los punteros inteligentes a menudo eliminan sus objetos cuando se eliminan. Los punteros ordinarios no lo hacen. Lo mismo ocurre cuando se hace que un puntero apunte a un objeto diferente. Algunos punteros inteligentes destruirán el objeto antiguo o lo destruirán si no tiene más referencias. Los punteros ordinarios no tienen esa inteligencia. Solo tienen una dirección y le permiten realizar operaciones en los objetos a los que apuntan al hacerlo específicamente.
Eso depende de la implementación de la lista vinculada. Las colecciones típicas destruyen todos sus objetos contenidos cuando son destruidos.
Por lo tanto, una lista vinculada de punteros normalmente destruiría los punteros, pero no los objetos a los que apuntan. (Lo cual puede ser correcto. Pueden ser referencias de otros punteros). Sin embargo, una lista enlazada diseñada específicamente para contener punteros podría eliminar los objetos por su propia destrucción.
Una lista vinculada de punteros inteligentes podría eliminar automáticamente los objetos cuando se eliminan los punteros, o hacerlo si no tuvieran más referencias. Depende de usted elegir las piezas que hacen lo que quiere.
Por supuesto. Un ejemplo sería si desea reemplazar un objeto con otro objeto del mismo tipo pero no desea liberar memoria solo para asignarlo nuevamente. Puede destruir el objeto antiguo en su lugar y construir uno nuevo en su lugar. (Sin embargo, generalmente esta es una mala idea).
fuente
new Foo()
con una 'F' mayúscula).Foo myfoo("foo")
no es el análisis más irritante, pero lochar * foo = "foo"; Foo myfoo(foo);
es.delete myFoo
llamarse antesFoo *myFoo = new Foo("foo");
? De lo contrario, eliminaría el objeto recién creado, ¿no?myFoo
antes de laFoo *myFoo = new Foo("foo");
línea. Esa línea crea una nueva variable llamadamyFoo
, que sombrea cualquier existente. Aunque en este caso, no existe ninguno ya que lomyFoo
anterior está en el alcance delif
, que ha finalizado.Otros ya han abordado los otros problemas, por lo que solo miraré un punto: ¿alguna vez desea eliminar manualmente un objeto?
La respuesta es sí. @DavidSchwartz dio un ejemplo, pero es bastante inusual. Daré un ejemplo que está bajo el capó de lo que muchos programadores de C ++ usan todo el tiempo:
std::vector
(ystd::deque
, aunque no se usa tanto).Como la mayoría de la gente sabe,
std::vector
asignará un bloque de memoria más grande cuando / si agrega más elementos de los que puede contener su asignación actual. Sin embargo, cuando hace esto, tiene un bloque de memoria que es capaz de contener más objetos de los que hay actualmente en el vector.Para administrar eso, lo que
vector
hace bajo las sábanas es asignar memoria sin procesar a través delAllocator
objeto (que, a menos que especifique lo contrario, significa que usa::operator new
). Luego, cuando usa (por ejemplo)push_back
para agregar un elemento alvector
, internamente el vector usa aplacement new
para crear un elemento en la parte (previamente) no utilizada de su espacio de memoria.Ahora, ¿qué sucede cuando / si tienes
erase
un elemento del vector? No puede simplemente usardelete
, eso liberaría todo su bloque de memoria; necesita destruir un objeto en esa memoria sin destruir ningún otro, o liberar ninguno de los bloques de memoria que controla (por ejemplo, si ustederase
5 elementos de un vector, luego inmediatamentepush_back
5 elementos más, se garantiza que el vector no se reasignará memoria cuando lo hagas.Para hacer eso, el vector destruye directamente los objetos en la memoria llamando explícitamente al destructor, no usando
delete
.Si, por casualidad, alguien más escribiera un contenedor usando almacenamiento contiguo más o menos como lo
vector
hace (o alguna variante de eso, comostd::deque
realmente lo hace), es casi seguro que querrá usar la misma técnica.Por ejemplo, consideremos cómo podría escribir código para un búfer circular circular.
A diferencia de los contenedores estándar, este usa
operator new
yoperator delete
directamente. Para uso real, probablemente quieras usar una clase de asignador, pero por el momento haría más para distraer que para contribuir (en mi opinión, de todos modos).fuente
new
, es responsable de llamardelete
. Cuando crea un objeto conmake_shared
, el resultadoshared_ptr
es responsable de llevar el recuento y llamardelete
cuando el recuento de uso llega a cero.new
(es decir, es un objeto de pila).new
.fuente
1) Los objetos no se crean 'mediante punteros'. Hay un puntero que se asigna a cualquier objeto que sea "nuevo". Suponiendo que esto es lo que quiere decir, si llama a 'eliminar' en el puntero, en realidad eliminará (y llamará al destructor en) el objeto que el puntero desreferencia. Si asigna el puntero a otro objeto, habrá una pérdida de memoria; nada en C ++ recogerá su basura por usted.
2) Estas son dos preguntas separadas. Una variable sale del alcance cuando el marco de pila en el que se declara se saca de la pila. Por lo general, esto es cuando dejas un bloque. Los objetos en un montón nunca salen de su alcance, aunque sus punteros en la pila pueden hacerlo. Nada en particular garantiza que se llamará a un destructor de un objeto en una lista vinculada.
3) No realmente. Puede haber Deep Magic que sugiera lo contrario, pero por lo general desea hacer coincidir sus 'nuevas' palabras clave con sus palabras clave 'eliminar' y poner todo lo necesario en su destructor para asegurarse de que se limpia correctamente. Si no hace esto, asegúrese de comentar el destructor con instrucciones específicas para cualquier persona que use la clase sobre cómo deben limpiar los recursos de ese objeto manualmente.
fuente
Para dar una respuesta detallada a la pregunta 3: sí, hay ocasiones (raras) en las que puede llamar al destructor explícitamente, en particular como la contraparte de una ubicación nueva, como observa dasblinkenlight.
Para dar un ejemplo concreto de esto:
El propósito de este tipo de cosas es desacoplar la asignación de memoria de la construcción de objetos.
fuente
Punteros : los punteros regulares no son compatibles con RAII. Sin un explícito
delete
, habrá basura. ¡Afortunadamente, C ++ tiene punteros automáticos que manejan esto por usted!Alcance : piense en cuándo una variable se vuelve invisible para su programa. Por lo general, esto es al final de
{block}
, como señala.Destrucción manual : nunca intente esto. Simplemente deje que el alcance y RAII hagan la magia por usted.
fuente
std::auto_ptr
está en desuso en C ++ 11, sí. Si el OP realmente tiene C ++ 11, debería usarlostd::unique_ptr
para propietarios únicos ostd::shared_ptr
para múltiples propietarios contados por referencia.std::queue<std::shared_ptr>?
he descubierto quepipe()
entre un hilo de productor y de consumidor hace que la concurrencia sea mucho más fácil, si la copia no es demasiado cara.Siempre que use "nuevo", es decir, adjunte una dirección a un puntero, o para decir, reclame espacio en el montón, debe "eliminarlo".
1.Sí, cuando borra algo, se llama al destructor.
2.Cuando se llama al destructor de la lista enlazada, se llama al destructor de sus objetos. Pero si son punteros, debe eliminarlos manualmente. 3. cuando el espacio sea reclamado por "nuevo".
fuente
Sí, se llama a un destructor (también conocido como dtor) cuando un objeto sale del alcance si está en la pila o cuando llama
delete
a un puntero a un objeto.Si el puntero se elimina mediante,
delete
se llamará al dtor. Si reasigna el puntero sin llamardelete
primero, obtendrá una pérdida de memoria porque el objeto todavía existe en la memoria en algún lugar. En el último caso, no se llama al dtor.Una buena implementación de lista enlazada llamará al dtor de todos los objetos de la lista cuando la lista se esté destruyendo (porque llamó a algún método para destruirla o se salió del alcance). Esto depende de la implementación.
Lo dudo, pero no me sorprendería si hubiera alguna circunstancia extraña por ahí.
fuente
Si el objeto no se crea a través de un puntero (por ejemplo, A a1 = A ();), se llama al destructor cuando se destruye el objeto, siempre cuando la función donde se encuentra el objeto está terminada. Por ejemplo:
se llama al destructor cuando el código se ejecuta en la línea "finalizar".
Si el objeto se crea a través de un puntero (por ejemplo, A * a2 = new A ();), se llama al destructor cuando se elimina el puntero (eliminar a2;). Si el usuario no elimina el punto de forma explícita o nueva dirección antes de eliminarla, se produjo la pérdida de memoria. Eso es un error.
En una lista enlazada, si usamos std :: list <>, no debemos preocuparnos por el desctructor o la pérdida de memoria porque std :: list <> ha terminado todos estos por nosotros. En una lista enlazada escrita por nosotros mismos, deberíamos escribir el desctructor y borrar el puntero explícitamente, de lo contrario provocará pérdida de memoria.
Rara vez llamamos a un destructor manualmente. Es una función que proporciona el sistema.
¡Disculpa mi pobre ingles!
fuente
Recuerde que el constructor de un objeto se llama inmediatamente después de que se asigna la memoria para ese objeto y mientras que el destructor se llama justo antes de desasignar la memoria de ese objeto.
fuente