Originalmente publiqué esto como una pregunta solo sobre destructores, pero ahora estoy agregando la consideración del constructor predeterminado. Aquí está la pregunta original:
Si quiero darle a mi clase un destructor que sea virtual, pero que por lo demás sea el mismo que generaría el compilador, puedo usar
=default
:class Widget { public: virtual ~Widget() = default; };
Pero parece que puedo obtener el mismo efecto con menos tipeo usando una definición vacía:
class Widget { public: virtual ~Widget() {} };
¿Hay alguna forma en que estas dos definiciones se comporten de manera diferente?
Según las respuestas publicadas para esta pregunta, la situación para el constructor predeterminado parece similar. Dado que casi no hay diferencia en el significado entre " =default
" y " {}
" para los destructores, ¿hay una diferencia similar en el significado entre estas opciones para los constructores predeterminados? Es decir, suponiendo que quiero crear un tipo donde los objetos de ese tipo serán creados y destruidos, ¿por qué querría decir
Widget() = default;
en vez de
Widget() {}
?
Pido disculpas si extender esta pregunta después de su publicación original está violando algunas reglas SO. Publicar una pregunta casi idéntica para los constructores predeterminados me pareció la opción menos deseable.
fuente
= default
es más explícito en la OMI, y es coherente con el soporte con constructores.std::has_trivial_destructor<Widget>::value
estrue
para el primero, perofalse
para el segundo. Tampoco sé cuáles son las implicaciones de eso. :)Respuestas:
Esta es una pregunta completamente diferente cuando se pregunta sobre constructores que sobre destructores.
Si su destructor es
virtual
, entonces la diferencia es insignificante, como señaló Howard . Sin embargo, si su destructor no era virtual , es una historia completamente diferente. Lo mismo es cierto para los constructores.El uso de la
= default
sintaxis para funciones miembro especiales (constructor predeterminado, copiar / mover constructores / asignación, destructores, etc.) significa algo muy diferente de simplemente hacer{}
. Con este último, la función se convierte en "proporcionada por el usuario". Y eso lo cambia todo.Esta es una clase trivial según la definición de C ++ 11:
Si intentas construir uno por defecto, el compilador generará un constructor por defecto automáticamente. Lo mismo ocurre con la copia / movimiento y la destrucción. Debido a que el usuario no proporcionó ninguna de estas funciones miembro, la especificación C ++ 11 lo considera una clase "trivial". Por lo tanto, es legal hacer esto, como recordar sus contenidos para inicializarlos, etc.
Esta:
Como su nombre indica, esto ya no es trivial. Tiene un constructor predeterminado proporcionado por el usuario. No importa si está vacío; En lo que respecta a las reglas de C ++ 11, este no puede ser un tipo trivial.
Esta:
Nuevamente, como su nombre lo indica, este es un tipo trivial. ¿Por qué? Porque le dijiste al compilador que generara automáticamente el constructor predeterminado. Por lo tanto, el constructor no es "proporcionado por el usuario". Y, por lo tanto, el tipo cuenta como trivial, ya que no tiene un constructor predeterminado proporcionado por el usuario.
La
= default
sintaxis está principalmente allí para hacer cosas como copiar constructores / asignaciones, cuando agrega funciones miembro que impiden la creación de tales funciones. Pero también desencadena un comportamiento especial del compilador, por lo que también es útil en constructores / destructores predeterminados.fuente
=default
funciones) y las funciones proporcionadas por el usuario (que es el caso de{}
). Tanto las funciones declaradas por el usuario como las proporcionadas por el usuario pueden evitar la generación de otra función miembro especial (por ejemplo, un destructor declarado por el usuario impide la generación de las operaciones de movimiento), pero solo una función especial proporcionada por el usuario hace que una clase no sea trivial. ¿Correcto?= default
parece ser útil para forzar al compilador a generar un constructor predeterminado a pesar de la presencia de otros constructores; el constructor predeterminado no se declara implícitamente si se proporcionan otros constructores declarados por el usuario.Ambos son no triviales.
Ambos tienen la misma especificación sin excepción, dependiendo de la especificación sin excepción de las bases y los miembros.
La única diferencia que estoy detectando hasta ahora es que si
Widget
contiene una base o miembro con un destructor inaccesible o eliminado:Entonces la
=default
solución se compilará, peroWidget
no será un tipo destructible. Es decir, si intenta destruir unWidget
, obtendrá un error en tiempo de compilación. Pero si no lo hace, tiene un programa que funciona.Otoh, si proporciona el destructor proporcionado por el usuario , las cosas no se compilarán si destruye o no
Widget
:fuente
=default;
el compilador no generará el destructor a menos que se use, y por lo tanto no desencadenará un error. Esto me parece extraño, aunque no sea necesariamente un error. No puedo imaginar que este comportamiento sea obligatorio en el estándar.La diferencia importante entre
y
es que el constructor predeterminado definido con
B() = default;
se considera no definido por el usuario . Esto significa que en caso de inicialización de valor como enSe llevará a cabo un tipo especial de inicialización que no utiliza un constructor en absoluto y para los tipos integrados esto dará como resultado una inicialización cero . En caso de que
B(){}
esto no ocurra. El estándar C ++ n3337 § 8.5 / 7 dicePor ejemplo:
posible resultado:
http://ideone.com/k8mBrd
fuente