destructor c++
¿Debo predeterminar los destructores virtuales? (1)
Tengo una clase abstracta que se declara como sigue:
class my_type {
public:
virtual ~my_type() = default;
virtual void do_something() = 0;
};
¿Se considera una buena práctica declarar el destructor de esta manera, con la palabra clave default
? ¿Hay alguna manera mejor?
Además, ¿es = 0
una forma moderna (C ++ 11) de no especificar una implementación predeterminada, o hay una forma mejor?
Sí, definitivamente puedes usar = default
para tales destructores. Especialmente si solo fueras a reemplazarlo con {}
. Creo que el = default
es mejor, porque es más explícito, por lo que inmediatamente llama la atención y no deja lugar a dudas.
Sin embargo, aquí hay un par de notas para tener en cuenta al hacerlo.
Cuando = default
un destructor = default
en el archivo de encabezado (ver edición) (o cualquier otra función especial para el caso), básicamente lo define en el encabezado. Al diseñar una biblioteca compartida, es posible que desee tener explícitamente el destructor proporcionado solo por la biblioteca en lugar de en el encabezado, de modo que pueda cambiarlo más fácilmente en el futuro sin requerir una reconstrucción del binario dependiente. Pero, de nuevo, eso es para cuando la pregunta no es simplemente si = default
o {}
.
EDITAR: Como Sean señaló claramente en los comentarios, también puede usar = default
fuera de la declaración de clase, que obtiene lo mejor de ambos mundos aquí.
La otra diferencia técnica crucial, es que la norma dice que una función explícitamente predeterminada que no se puede generar, simplemente no se generará. Considere el siguiente ejemplo:
struct A { ~A() = delete; };
struct B : A { ~B() {}; }
Esto no se compilaría, ya que obliga al compilador a generar el código especificado (y sus requisitos implícitos, como llamar al destructor de A) para el destructor de B, y no puede hacerlo, porque se elimina el destructor de A. Considera esto, sin embargo:
struct A { ~A() = delete; };
struct B : A { ~B() = default; }
Esto, de hecho, se compilaría, porque el compilador ve que ~B()
no se puede generar, por lo que simplemente no lo genera, y lo declara como eliminado. Esto significa que solo obtendrá el error cuando intente usar / call B::~B()
.
Esto tiene al menos dos implicaciones que debe tener en cuenta:
- Si desea obtener el error simplemente al compilar cualquier cosa que incluya la declaración de clase, no lo obtendrá, ya que es técnicamente válido.
- Si inicialmente siempre usas
= default
para tales destructores, entonces no tendrás que preocuparte por la eliminación del destructor de tu súper clase, si resulta que está bien y nunca lo usas realmente. Es un tipo de uso exótico, pero en ese sentido es más correcto y está preparado para el futuro. Solo obtendrás el error si realmente usas el destructor; de lo contrario, estarás solo.
Por lo tanto, si opta por un enfoque de programación defensivo, quizás desee considerar simplemente usar {}
. De lo contrario, es probable que esté mejor = default
, ya que se adhiere mejor a tomar las instrucciones programáticas mínimas necesarias para obtener una base de código correcta y de trabajo, y evitar consecuencias no deseadas 1 .
En cuanto a = 0
: Sí, esa sigue siendo la forma correcta de hacerlo. Pero tenga en cuenta que, de hecho, no especifica que no hay "implementación predeterminada", sino que (1) La clase no es instantánea; y (2) Cualquier clase derivada debe anular esa función (aunque puede usar una implementación opcional proporcionada por la superclase). En otras palabras, puede definir una función y declararla como virtual pura. Aquí hay un ejemplo:
struct A { virtual ~A() = 0; }
A::~A() = default;
Esto asegurará estas restricciones en A (y su destructor).
1) Un buen ejemplo de por qué esto puede ser útil en formas inesperadas, es cómo algunas personas siempre usaron return
entre paréntesis, y luego C ++ 14 agregó decltype(auto)
que esencialmente creó una diferencia técnica entre su uso con y sin paréntesis.