Hará lo mismo (nada, en esencia). Pero no es lo mismo que si no lo escribieras. Porque escribir el destructor requerirá un destructor de clase base en funcionamiento. Si el destructor de la clase base es privado o si por algún otro motivo no puede invocarse, entonces su programa está defectuoso. Considere esto
struct A { private: ~A(); };
struct B : A { };
Eso está bien, siempre y cuando su no requieren para destruir un objeto de tipo B (y por lo tanto, implícitamente del tipo A) - como si nunca llama a eliminar en un objeto creado de forma dinámica, o nunca creaste un objeto de ella en primer lugar. Si lo hace, entonces el compilador mostrará un diagnóstico apropiado. Ahora bien, si usted proporciona una forma explícita
struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } };
que uno va a tratar de llamar implícitamente el destructor de la clase base, y causará un diagnóstico ya en tiempo de definición de ~B
.
Hay otra diferencia que se centra en la definición del destructor y las llamadas implícitas a los miembros destructores.Considere a este miembro puntero inteligente
struct C;
struct A {
auto_ptr<C> a;
A();
};
Asumamos que el objeto de tipo C
se crea en la definición del constructor de la a en el archivo .cpp
, que también contiene la definición de la estructura C
. Ahora, si usa struct A
y requiere la destrucción de un objeto A
, el compilador proporcionará una definición implícita del destructor, como en el caso anterior. Ese destructor también llamará implícitamente al destructor del objeto auto_ptr. Y eso eliminará el puntero que contiene, que apunta al objeto C
, ¡sin conocer la definición de C
! Eso apareció en el archivo .cpp
donde se define el constructor de struct A.
Esto en realidad es un problema común en la implementación del idioma pimpl. La solución aquí es agregar un destructor y proporcionar una definición vacía de él en el archivo .cpp
, donde se define la estructura C
. En el momento en que invoca el destructor de su miembro, conocerá la definición de struct C
y puede llamar correctamente a su destructor.
struct C;
struct A {
auto_ptr<C> a;
A();
~A(); // defined as ~A() { } in .cpp file, too
};
Tenga en cuenta que boost::shared_ptr
no tiene ese problema: En su lugar, requiere un tipo completo cuando su constructor se invoca en ciertas maneras.
Otro punto donde hace una diferencia en el C++ actual es cuando desea utilizar memset
y amigos en un objeto que tiene un usuario declarado destructor. Dichos tipos ya no son POD (datos antiguos simples), y no se permite copiarlos en bits. Tenga en cuenta que esta restricción no es realmente necesaria, y la próxima versión de C++ ha mejorado la situación en este sentido, de modo que le permite copiar aún dichos tipos, siempre que no se realicen otros cambios más importantes.
Como ha pedido a los constructores: Bueno, para estas cosas, las mismas cosas son ciertas. Tenga en cuenta que los constructores también contienen llamadas implícitas a los destructores. En cosas como auto_ptr, estas llamadas (incluso si no se realizan en tiempo de ejecución, la posibilidad pura ya importa aquí) hará el mismo daño que para los destructores, y ocurrirá cuando algo en el constructor arroje, el compilador debe llamar al destructor. de los miembros. This answer hace un cierto uso de la definición implícita de constructores por defecto.
Además, lo mismo es cierto para la visibilidad y la PODness que he dicho sobre el destructor anterior.
Hay una diferencia importante con respecto a la inicialización. Si pone un constructor declarado por el usuario, su tipo ya no recibe la inicialización de valor de los miembros, y le corresponde a su constructor hacer cualquier inicialización que sea necesaria. Ejemplo:
struct A {
int a;
};
struct B {
int b;
B() { }
};
En este caso, el siguiente es siempre verdad
assert(A().a == 0);
Mientras que el siguiente es un comportamiento indefinido, porque b
no se ha inicializado (el constructor que omite). El valor puede ser cero, pero también puede ser cualquier otro valor extraño. Intentar leer de un objeto no inicializado provoca un comportamiento indefinido.
assert(B().b == 0);
Esto también es cierto para el uso de esta sintaxis en new
, como new A()
(tenga en cuenta los paréntesis al final - si se omiten valor de inicialización no se hace, y puesto que no hay ningún usuario declaró constructor que podría inicializarlo , a
se dejarán sin inicializar).
He modificado esta pregunta un poco para convertir la edición posterior a una verdadera parte de la pregunta. Si hay errores de sintaxis en las partes que edité, grítame, no el asker original. @ Andrew, si sientes que he cambiado demasiado tu pregunta, no dudes en revertirla; si le gusta el cambio pero cree que no fue suficiente, obviamente puede editar su propia pregunta. –