Destructores (C++)Destructors (C++)

Un destructor es una función miembro que se invoca automáticamente cuando el objeto sale del ámbito o se destruye explícitamente mediante una llamada a delete .A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete. Un destructor tiene el mismo nombre que la clase, precedido por una tilde ( ~ ).A destructor has the same name as the class, preceded by a tilde (~). Por ejemplo, el destructor de la clase String se declara como: ~String().For example, the destructor for class String is declared: ~String().

Si no define un destructor, el compilador proporcionará uno predeterminado; para muchas clases es suficiente.If you do not define a destructor, the compiler will provide a default one; for many classes this is sufficient. Solo necesita definir un destructor personalizado cuando la clase almacena los identificadores de los recursos del sistema que deben liberarse, o los punteros que poseen la memoria a la que apuntan.You only need to define a custom destructor when the class stores handles to system resources that need to be released, or pointers that own the memory they point to.

Considere la siguiente declaración de una clase String:Consider the following declaration of a String class:

// spec1_destructors.cpp
#include <string>

class String {
public:
   String( char *ch );  // Declare constructor
   ~String();           //  and destructor.
private:
   char    *_text;
   size_t  sizeOfText;
};

// Define the constructor.
String::String( char *ch ) {
   sizeOfText = strlen( ch ) + 1;

   // Dynamically allocate the correct amount of memory.
   _text = new char[ sizeOfText ];

   // If the allocation succeeds, copy the initialization string.
   if( _text )
      strcpy_s( _text, sizeOfText, ch );
}

// Define the destructor.
String::~String() {
   // Deallocate the memory that was previously reserved
   //  for this string.
   delete[] _text;
}

int main() {
   String str("The piper in the glen...");
}

En el ejemplo anterior, el destructor String::~String usa el delete operador para desasignar el espacio asignado dinámicamente para el almacenamiento de texto.In the preceding example, the destructor String::~String uses the delete operator to deallocate the space dynamically allocated for text storage.

Declarar destructoresDeclaring destructors

Los destructores son funciones con el mismo nombre que la clase pero precedidos por una tilde (~).Destructors are functions with the same name as the class but preceded by a tilde (~)

Varias reglas rigen la declaración de destructores.Several rules govern the declaration of destructors. Destructores:Destructors:

  • No aceptan argumentos.Do not accept arguments.

  • No devuelva un valor (o void ).Do not return a value (or void).

  • No se puede declarar como const , volatile o static .Cannot be declared as const, volatile, or static. Sin embargo, se pueden invocar para la destrucción de objetos declarados como const , volatile o static .However, they can be invoked for the destruction of objects declared as const, volatile, or static.

  • Se puede declarar como virtual .Can be declared as virtual. Mediante los destructores virtuales, puede destruir objetos sin conocer su tipo; se invoca el destructor correcto para el objeto mediante el mecanismo de función virtual.Using virtual destructors, you can destroy objects without knowing their type — the correct destructor for the object is invoked using the virtual function mechanism. Observe que los destructores también se pueden declarar como funciones virtuales puras para las clases abstractas.Note that destructors can also be declared as pure virtual functions for abstract classes.

Usar destructoresUsing destructors

Se llama a los destructores cuando se produce alguno de los eventos siguientes:Destructors are called when one of the following events occurs:

  • Un objeto local (automático) con ámbito de bloque sale de ámbito.A local (automatic) object with block scope goes out of scope.

  • Un objeto asignado mediante el new operador se desasigna explícitamente mediante delete .An object allocated using the new operator is explicitly deallocated using delete.

  • La duración de un objeto temporal termina.The lifetime of a temporary object ends.

  • Un programa termina y hay objetos globales o estáticos.A program ends and global or static objects exist.

  • Se llama explícitamente al destructor mediante el nombre completo de la función de destructor.The destructor is explicitly called using the destructor function's fully qualified name.

Los destructores pueden llamar libremente a funciones miembro de clase y acceder a datos de miembros de clase.Destructors can freely call class member functions and access class member data.

Hay dos restricciones en el uso de destructores:There are two restrictions on the use of destructors:

  • No se puede tomar su dirección.You cannot take its address.

  • Las clases derivadas no heredan el destructor de su clase base.Derived classes do not inherit the destructor of their base class.

Orden de destrucciónOrder of destruction

Cuando un objeto sale del ámbito o se elimina, la secuencia de eventos para su completa destrucción es la siguiente:When an object goes out of scope or is deleted, the sequence of events in its complete destruction is as follows:

  1. Se llama al destructor de clase y se ejecuta el cuerpo de la función destructora.The class's destructor is called, and the body of the destructor function is executed.

  2. Los destructores de los objetos miembro no estáticos se llaman en el orden inverso al que aparecen en la declaración de clase.Destructors for nonstatic member objects are called in the reverse order in which they appear in the class declaration. La lista de inicialización de miembros opcional utilizada en la construcción de estos miembros no afecta al orden de construcción o destrucción.The optional member initialization list used in construction of these members does not affect the order of construction or destruction.

  3. Los destructores para las clases base no virtuales se llaman en el orden inverso a la declaración.Destructors for non-virtual base classes are called in the reverse order of declaration.

  4. Los destructores para las clases base virtuales se llaman en el orden inverso al de la declaración.Destructors for virtual base classes are called in the reverse order of declaration.

// order_of_destruction.cpp
#include <cstdio>

struct A1      { virtual ~A1() { printf("A1 dtor\n"); } };
struct A2 : A1 { virtual ~A2() { printf("A2 dtor\n"); } };
struct A3 : A2 { virtual ~A3() { printf("A3 dtor\n"); } };

struct B1      { ~B1() { printf("B1 dtor\n"); } };
struct B2 : B1 { ~B2() { printf("B2 dtor\n"); } };
struct B3 : B2 { ~B3() { printf("B3 dtor\n"); } };

int main() {
   A1 * a = new A3;
   delete a;
   printf("\n");

   B1 * b = new B3;
   delete b;
   printf("\n");

   B3 * b2 = new B3;
   delete b2;
}

Output: A3 dtor
A2 dtor
A1 dtor

B1 dtor

B3 dtor
B2 dtor
B1 dtor

Clases base virtualesVirtual base classes

Los destructores de las clases base virtuales se llaman en orden inverso al de su aparición en un gráfico acíclico dirigido (recorrido con prioridad de profundidad, de izquierda a derecha y en postorden).Destructors for virtual base classes are called in the reverse order of their appearance in a directed acyclic graph (depth-first, left-to-right, postorder traversal). La ilustración siguiente representa un gráfico de herencia.the following figure depicts an inheritance graph.

Gráfico de herencia que muestra clases base virtualesInheritance graph that shows virtual base classes
Gráfico de herencia que muestra clases base virtualesInheritance graph that shows virtual base classes

A continuación se enumeran los encabezados de las clases que se muestran en la ilustración.The following lists the class heads for the classes shown in the figure.

class A
class B
class C : virtual public A, virtual public B
class D : virtual public A, virtual public B
class E : public C, public D, virtual public B

Para determinar el orden de destrucción de las clases base virtuales de un objeto de tipo E, el compilador compila una lista aplicando el algoritmo siguiente:To determine the order of destruction of the virtual base classes of an object of type E, the compiler builds a list by applying the following algorithm:

  1. Recorrer el gráfico izquierdo, desde el punto más profundo del gráfico (en este caso, E).Traverse the graph left, starting at the deepest point in the graph (in this case, E).

  2. Realizar recorridos hacia la izquierda hasta que se hayan visitado todos los nodos.Perform leftward traversals until all nodes have been visited. Anotar el nombre del nodo actual.Note the name of the current node.

  3. Revisitar el nodo anterior (abajo y a la derecha) para averiguar si el nodo recordado es una clase base virtual.Revisit the previous node (down and to the right) to find out whether the node being remembered is a virtual base class.

  4. Si el nodo recordado es una clase base virtual, examinar la lista para ver si ya se ha especificado.If the remembered node is a virtual base class, scan the list to see whether it has already been entered. Si no es una clase base virtual, omitirla.If it is not a virtual base class, ignore it.

  5. Si el nodo recordado no está aún en la lista, agregarla al final de la lista.If the remembered node is not yet in the list, add it to the bottom of the list.

  6. Recorrer el gráfico hacia arriba y a lo largo de la ruta siguiente a la derecha.Traverse the graph up and along the next path to the right.

  7. Vaya al paso 2.Go to step 2.

  8. Cuando se agote la última ruta ascendente, anotar el nombre del nodo actual.When the last upward path is exhausted, note the name of the current node.

  9. Vaya al paso 3.Go to step 3.

  10. Continuar este proceso hasta que el nodo inferior sea de nuevo el nodo actual.Continue this process until the bottom node is again the current node.

Por consiguiente, para la clase E, el orden de destrucción es:Therefore, for class E, the order of destruction is:

  1. La clase base no virtual E .The non-virtual base class E.

  2. La clase base no virtual D .The non-virtual base class D.

  3. La clase base no virtual C .The non-virtual base class C.

  4. La clase base virtual B.The virtual base class B.

  5. La clase base virtual A.The virtual base class A.

Este proceso produce una lista ordenada de entradas únicas.This process produces an ordered list of unique entries. Ningún nombre de clase aparece dos veces.No class name appears twice. Una vez construida la lista, se recorre en orden inverso y se llama al destructor para cada una de las clases de la lista, desde la última hasta la primera.Once the list is constructed, it is walked in reverse order, and the destructor for each of the classes in the list from the last to the first is called.

El orden de construcción o de destrucción es importante sobre todo cuando los constructores o destructores de una clase se basan en que el otro componente se cree primero o persista más tiempo; por ejemplo, si el destructor para A (en la ilustración anterior) se basa en que B continúa estando presente cuando se ejecute el código o viceversa.The order of construction or destruction is primarily important when constructors or destructors in one class rely on the other component being created first or persisting longer — for example, if the destructor for A (in the figure shown above) relied on B still being present when its code executed, or vice versa.

Tales interdependencias entre clases en un gráfico de herencia son inherentemente peligrosas, porque las últimas clases derivadas pueden cambiar cuál es la ruta más a la izquierda y, en consecuencia, pueden cambiar el orden de construcción y destrucción.Such interdependencies between classes in an inheritance graph are inherently dangerous because classes derived later can alter which is the leftmost path, thereby changing the order of construction and destruction.

Clases base no virtualesNon-virtual base classes

Los destructores para las clases base no virtuales se llaman en el orden inverso en el que se declaran los nombres de clase base.The destructors for non-virtual base classes are called in the reverse order in which the base class names are declared. Considere la siguiente declaración de clase:Consider the following class declaration:

class MultInherit : public Base1, public Base2
...

En el ejemplo anterior, el destructor para Base2 se invoca antes que el destructor para Base1.In the preceding example, the destructor for Base2 is called before the destructor for Base1.

Llamadas de destructor explícitasExplicit destructor calls

Raras veces se necesita llamar explícitamente al destructor.Calling a destructor explicitly is seldom necessary. Sin embargo, puede ser útil realizar la limpieza de los objetos colocados en direcciones absolutas.However, it can be useful to perform cleanup of objects placed at absolute addresses. Normalmente, estos objetos se asignan mediante un operador definido por el usuario new que toma un argumento de colocación.These objects are commonly allocated using a user-defined new operator that takes a placement argument. El delete operador no puede desasignar esta memoria porque no está asignada desde el almacén gratuito (para obtener más información, vea los operadores New y DELETE).The delete operator cannot deallocate this memory because it is not allocated from the free store (for more information, see The new and delete Operators). Sin embargo, una llamada al destructor puede realizar la limpieza adecuada.A call to the destructor, however, can perform appropriate cleanup. Para llamar explícitamente al destructor para un objeto, s, de clase String, utilice una de las instrucciones siguientes:To explicitly call the destructor for an object, s, of class String, use one of the following statements:

s.String::~String();     // non-virtual call
ps->String::~String();   // non-virtual call

s.~String();       // Virtual call
ps->~String();     // Virtual call

La notación para las llamadas explícitas a destructores, que se muestra anteriormente, puede utilizarse con independencia de que el tipo defina un destructor.The notation for explicit calls to destructors, shown in the preceding, can be used regardless of whether the type defines a destructor. Esto permite realizar llamadas explícitas sin saber si un destructor está definido para el tipo.This allows you to make such explicit calls without knowing if a destructor is defined for the type. Una llamada explícita a un destructor donde no se ha definido ninguna no tiene ningún efecto.An explicit call to a destructor where none is defined has no effect.

Programación sólidaRobust programming

Una clase necesita un destructor Si adquiere un recurso y, para administrar el recurso, es probable que tenga que implementar un constructor de copias y una asignación de copia.A class needs a destructor if it acquires a resource, and to manage the resource safely it probably has to implement a copy constructor and a copy assignment.

Si el usuario no define estas funciones especiales, el compilador las define de forma implícita.If these special functions are not defined by the user, they are implicitly defined by the compiler. Los operadores de asignación y constructores generados implícitamente realizan una copia miembro a miembro superficial, que es casi seguro si un objeto está administrando un recurso.The implicitly generated constructors and assignment operators perform shallow, memberwise copy, which is almost certainly wrong if an object is managing a resource.

En el ejemplo siguiente, el constructor de copias generado implícitamente hará que los punteros str1.text y str2.text hagan referencia a la misma memoria y, cuando se devuelva desde copy_strings() , esa memoria se eliminará dos veces, lo que es un comportamiento indefinido:In the next example, the implicitly generated copy constructor will make the pointers str1.text and str2.text refer to the same memory, and when we return from copy_strings(), that memory will be deleted twice, which is undefined behavior:

void copy_strings()
{
   String str1("I have a sense of impending disaster...");
   String str2 = str1; // str1.text and str2.text now refer to the same object
} // delete[] _text; deallocates the same memory twice
  // undefined behavior

La definición explícita de un destructor, un constructor de copias o un operador de asignación de copia evita la definición implícita del constructor de movimiento y del operador de asignación de movimiento.Explicitly defining a destructor, copy constructor, or copy assignment operator prevents implicit definition of the move constructor and the move assignment operator. En este caso, no proporcionar operaciones de movimiento es normalmente, si la copia es costosa, una oportunidad de optimización perdida.In this case, failing to provide move operations is usually, if copying is expensive, a missed optimization opportunity.

Consulta tambiénSee also

Constructores de copia y operadores de asignación de copiaCopy Constructors and Copy Assignment Operators
Constructores de movimiento y operadores de asignación de movimientoMove Constructors and Move Assignment Operators