Espacios de nombres
Variantes
Acciones

Destructores

De cppreference.com
< cpp‎ | language
 
 
Lenguaje C++
Temas generales
Control de flujo
Instrucciones de ejecución condicionales
Instrucciones de iteración (bucles)
Declaraciones de salto
Funciones
Declaración de funciones
Declaración de funciones lambda
Especificador inline
Especificación de excepciones(hasta C++20)
Especificador noexcept(C++11)
Excepciones
Espacios de nombres
Tipos
Especificadores
decltype(C++11)
auto(C++11)
alignas(C++11)
Especificadores de duración de almacenamiento
Inicialización
Expresiones
Representaciones alternas
Literales
Booleanos - Enteros - De punto flotante
De carácter - De cadena - nullptr(C++11)
Definidos por el usuario(C++11)
Utilidades
Atributos(C++11)
Tipos
Declaración de typedef
Declaración de alias de tipo(C++11)
Conversiones
Conversiones implícitas - Conversiones explícitas
static_cast - dynamic_cast
const_cast - reinterpret_cast
Asignación de memoria
Clases
Propiedades de funciones específicas de la clase
Funciones miembro especiales
Plantillas
Misceláneos
 

Un destructor es una función miembro especial que se llama cuando la duración de un objeto termina. El propósito del destructor es liberar los recursos que el objeto pudo haber adquirido durante su duración.

Un destructor no debe ser una corrutina. (desde C++20)

Contenido

[editar]Sintaxis

~nombre_de_clase(); (1)
virtual~nombre_de_clase(); (2)
sec-decl-especificadores(opcional)~nombre_de_clase() = default; (3) (desde C++11)
sec-decl-especificadores(opcional)~nombre_de_clase() = delete; (4) (desde C++11)
atrib(opcional)sec-decl-especificadores(opcional)expresión-id(void(opcional))except(opcional)atrib(opcional)cláusula-requiere(opcional); (5)
1) Declaración típica de un destructor potencial(desde C++20)
2) Un destructor virtual se requiere habitualmente en una clase base
3) Forzar a que el compilador genere un destructor
4) Deshabilitar el destructor implícito
5) Sintaxis formal de una declaración de destructor potencial(desde C++20)
sec-decl-especificadores - friend, inline, virtual,constexpr, consteval(desde C++20)
expresión-id - Dentro de una definición de clase, el símbolo ~ seguido de nombre_de_clase. Dentro de una plantilla de clase, el símbolo ~ seguido del nombre de la instanciación actual de la plantilla. En el ámbito del espacio de nombres o en una declaración friend dentro de una clase distinta, especificador de nombre anidado seguido del símbolo ~ seguido de nombre_de_clase que es la misma clase que la nombrada por el especificador de nombre anidado. De cualquier forma, el nombre debe ser el nombre actual de la clase o plantilla y no una definición de tipo (typedef). La expresión-id completa puede estar rodeada por paréntesis, lo que no cambia su significado.
atrib(C++11) - Secuencia opcional de cualquier número de atributos
except - Especificación de excepción como en cualquier declaración de función (ya sea la especificación de excepción dinámica(en desuso)(eliminado en C++17) o la especificación noexcept(C++11)).

Excepto que si no se proporciona una especificación de excepción explícitamente, se considera que la especificación de excepción es una que se usaría por el destructor declarado implícitamente (véase abajo). En la mayoría de los casos, esta es noexcept(true). Es por esto que un destructor que lanza excepciones debe declararse explícitamente noexcept(false).

(desde C++11)


cláusula-requiere - (desde C++20) cláusula-requiere que declara las restricciones asociadas para el destructor potencial, que deben cumplirse para que el destructor potencial sea seleccionado como destructor

.

[editar]Explicación

Se llama al destructor siempre que termine la duración de un objeto, lo que incluye

  • la salida de un hilo, para objetos con duración de almacenamiento local al hilo;
(desde C++11)
  • el fin del ámbito, para objetos con duración de almacenamiento automática y para los temporales cuya vida se extendió al vincularlos con una referencia;
  • la expresión delete, para objetos con duración de almacenamiento dinámica;
  • el final de la expresión completa, para temporales sin nombre;
  • el desenredo de la pila, para objetos con duración de almacenamiento automática cuando una excepción se escapa de su bloque sin ser capturada.

El destructor también puede ser llamado directamente. Por ejemplo, para destruir un objeto que se construyó utilizando new de ubicación o a través una función miembro de asignación de memoria tal como std::allocator::destroy(), para destruir un objeto que se construyó a través del asignador de memoria. Observa que llamar a un destructor directamente para un objeto ordinario, tal como una variable local, invoca un comportamiento indefinido cuando el destructor se llama de nuevo, al final del ámbito.

En contextos genéricos, la sintaxis de llamada al destructor puede utilizarse con un objeto de tipo no-clase. Esto se conoce como un llamada a pseudodestructor: véase operador de acceso a miembro.

Destructor potencial

Una clase puede tener uno o más destructores potenciales, uno de los cuales se selecciona como el destructor de la clase.

Para determinar que destructor potencial es el destructor, al final de la definición de la clase se realiza la resolución de sobrecarga entre los destructores potenciales declarados en la clase con una lista de argumentos vacía. Si falla la resolución de sobrecarga, el programa está mal formado. La selección del destructor no hace uso odr del destructor seleccionado, y el destructor seleccionado puede ser eliminado.

Todos los destructores potenciales son funciones miembro especiales. Si no se proporciona un destructor potencial declarado por el usuario para la clase T, el compilador siempre declarará uno (véase más abajo), y el destructor potencial declarado implícitamente también es el destructor de T.

(desde C++20)

[editar]Destructor implícitamente declarado

Si no se proporciona un destructor potencial(desde C++20) declarado por el usuario para un tipo clase (struct, class, o union), el compilador siempre declarará un destructor como un miembro inline public de su clase.

Al igual que cualquier función especial implícitamente declarada, la especificación de excepción del destructor implícitamente declarado no lanza, a menos que el destructor de cualquier base o miembro potencialmente construidos potencialmente lancen(desde C++17)la definición implícita invocaría directamente a una función con una especificación de excepción distinta(hasta C++17). En la práctica, los destructores implícitos son noexcept, a menos que la clase esté "envenenada" por una base o miembro cuyo destructor sea noexcept(false).

[editar]Destructor implícitamente declarado eliminado

El destructor implícitamente declarado o explícito por defecto para la clase T es indefinido (hasta C++11)definido como eliminado(desde C++11) si cualquiera de lo siguiente es verdadero:

  • T tiene un dato miembro no estático que no puede destruirse (tiene un destructor eliminado o inaccesible);
  • T tiene una clase base directa o virtual que no puede destruirse (tiene destructores eliminados o inaccesibles);
  • T es una unión y tiene un miembro variante con un destructor no trivial;
(desde C++11)
  • el destructor implícitamente declarado es virtual (porque la clase base tiene un destructor virtual) y la búsqueda de la función de desasignación de memoria (operator delete() resulta en una llamada a una función ambigua, eliminada o inaccesible.

Un destructor potencial predeterminado explícito para T se define como eliminado si no es el destructor para T.

(desde C++20)

[editar]Destructor trivial

El destructor para una clase T es trivial si todo lo siguiente es verdadero:

  • El destructor no es proporcionado por el usuario (que quiere decir que es ya sea implícitamente declarado, o explícitamente definido como por defecto (=default) en su primera declaración).
  • El destructor no es virtual (es decir, el destructor de la clase base no es virtual).
  • Todas las clases base directas tienen destructores triviales.
  • Todos los datos miembro no estáticos de tipo clase (o array de tipo clase) tienen destructores triviales.

Un destructor trivial es un destructor que no realiza ninguna acción. Los objetos con destructores triviales no requieren una expresión delete y puede disponerse de ellos simplemente desasignando su almacenamiento. Todos los tipos de datos compatibles con el lenguaje C (tipos POD, o plain-old data types) son trivialmente destruibles.

[editar]Destructor implícitamente definido

Si un destructor implícitamente declarado no está eliminado, está implícitamente definido (es decir, un cuerpo de función es generado y compilado) por el compilador cuando hay uso ODR. Este destructor implícitamente definido tiene un cuerpo vacío. Si esto satisface los requerimientos de un destructor constexpr, el destructor generado es constexpr.(desde C++20)

[editar]Secuencia de destrucción

Tanto para los destructores definidos por el usuario como para los destructores implícitamente definidos, después de ejecutar el cuerpo del destructor y destruir los objetos automáticos alojados en el cuerpo, el compilador llama a los destructores para todos los miembros de datos no estáticos, no variantes, de la clase, en el orden inverso de su declaración. Luego llama a los destructores de todas las clases base no virtuales directas en orden inverso al de la construcción (que a su vez llama a los destructores de sus miembros y sus clases base, etc), y luego, si este objeto es de la clase más derivada, llama a los destructores de todas las bases virtuales.

Incluso cuando se llama al destructor directamente (p. ej., obj.~Foo();), la instrucción return en ~Foo() no devuelve el control al llamador de inmediato: primero llama a todos esos destructores miembros y base.

[editar]Destructores virtuales

La eliminación de un objeto a través de un puntero a la base invoca un comportamiento indefinido, a menos que el destructor de la clase base sea virtual:

class Base {public:virtual ~Base(){}};   class Derivada :public Base {};   Base* b = new Derivada; delete b;// seguro

Una guía común es que el destructor de la clase base debe ser ya sea público y virtual o protegido y no virtual

[editar]Destructores virtuales puros

Un destructor potencial(desde C++20) puede declararse virtual puro, por ejemplo en una clase base que necesita hacerse abstracta, pero no tiene otras funciones adecuadas que puedan declararse virtuales puras. Un destructor virtual puro debe tener una definición, ya que todos los destructores de las clases base siempre se llaman cuando la clase derivada se destruye:

class BaseAbstracta {public:virtual ~ BaseAbstracta()=0;};   BaseAbstracta::~ BaseAbstracta(){}   class Derivada :public BaseAbstracta {};   // BaseAbstracta obj; // ERROR de compilación Derivada obj;// de acuerdo

[editar]Excepciones

Como cualquier otra función, un destructor puede terminar lanzando una excepción(esto habitualmente requiere que se declare explícitamente noexcept(false))(desde C++11), sin embargo, si sucede que se llama a este destructor durante el desenredo de pila, en su lugar se llama a std::terminate.

Aunque a veces puede usarse a std::uncaught_exception para detectar un desenredo de pila en curso, en general se considera una mala práctica permitir que cualquier destructor termine lanzando una excepción. Sin embargo, esta funcionalidad se utiliza por algunas bibliotecas, como SOCI y Galera 3, que dependen de la habilidad de los destructores de temporales sin nombre de lanzar excepciones al final de la expresión completa que construye el temporal.

std::experimental::scope_success en la Biblioteca de fundamentos TS v3 puede tener un destructor que potencialmente lance, que lanza una excepción cuando se regresa del ámbito normalmente y la función de salida lanza una excepción.

[editar]Ejemplo

#include <iostream>   struct A {int i;   A (int n ): i ( n ){std::cout<<"ctor a"<< i <<'\n';}   ~A(){std::cout<<"dtor a"<< i <<'\n';}};   A a0(0);   int main(){ A a1(1); A* p;   {// ámbito anidado A a2(2); p = new A(3);}// a2 fuera de ámbito   delete p;// llama al destructor de a3}

Salida:

ctor a0 ctor a1 ctor a2 ctor a3 dtor a2 dtor a3 dtor a1 dtor a0

[editar]Informe de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 193 C++98 no se especificaba si los objetos automáticos en un destructor
se destruyen antes o después de la destrucción de los
subobjetos base y miembros de la clase
se destruyen antes de destruir esos
subobjetos
CWG 344 C++98 la sintaxis del declarador de destructor era defectuosa (tiene el
mismo problema que CWG issue 194 y CWG issue 263)
cambió la sintaxis a una sintaxis de
declarador de función especializada
CWG 1241 C++98 los miembros estáticos pueden destruirse justo después de
la ejecución del destructor
solo destruye miembros no
estáticos
CWG 2180 C++98 un destructor para la clase X llama a los destructores para las
clases base directas virtuales de X
esos destructores no se llaman

[editar]Véase también

close