c++ - que - Invocación virtual pura de constructor y destructor
que es un constructor y un destructor en c++ (5)
El estándar de C ++ dice: la invocación de una función virtual pura de un constructor o destructor está prohibida. ¿Cuál es la razón para esto? ¿Por qué debería el estándar colocar una restricción como esta?
Desde un antiguo borrador de C ++ Standard, pero las distinciones relevantes que dibujaré siguen siendo relevantes:
10.4-6 Las funciones miembro pueden ser llamadas desde un constructor (o destructor) de una clase abstracta ; el efecto de realizar una llamada virtual (class.virtual) a una función virtual pura directa o indirectamente para el objeto que se está creando (o destruyendo) a partir de dicho constructor (o destructor) no está definido.
Esto es sutilmente diferente de lo que está afirmando, ya que el contexto de la frase pre-punto y coma es implícitamente relevante para el post-. Reformulando:
undefined behaviour happens when an abstract class''s constructor or destructor calls one of its own member functions that is (still) pure virtual.
Incluyo el calificador "(todavía)" ya que las funciones virtuales puras reciben definiciones (y dejan de ser "puras") en algún punto de la jerarquía de clases. Esto es un poco extraño, pero considera esta parte del Estándar:
Una clase es abstracta si tiene al menos una función virtual pura. [Nota: tal función podría heredarse: ver a continuación. ]
Claramente, las clases derivadas con definiciones para la función que era puramente virtual en la base no son necesariamente abstractas. La afirmación anterior solo puede ser verdadera si el sentido de "pureza" no se aplica universalmente a la función virtual en sí, sino a los niveles de jerarquía de clases en los que permanece pura.
La consecuencia: si la función -desde el punto de vista del nivel constructor / destructor de la llamada en la jerarquía- ya se ha definido, se puede invocar con un comportamiento bien definido.
Con esta comprensión de lo que no está definido en el Estándar, podemos volver a sus preguntas: "¿Cuál es el motivo de esto? ¿Por qué debería el Estándar colocar una restricción como esta?"
El quid de la razón es que una clase base debe estar completamente construida antes de que un contructor de clase derivado se active, ya que la construcción de la clase derivada puede operar en el objeto base. De forma similar, un destructor base debe ejecutarse después del destructor derivado porque este último aún puede desear acceder al objeto base. Dado este orden necesario, el compilador no puede enviar de forma segura a la función virtual de clase derivada dado que el constructor de la clase derivada aún no se ha ejecutado para establecer el estado miembro de clase derivado, o el destructor de la clase derivada ya ha invocado destructores para los datos adicionales miembros y una vez más el estado del objeto ya no está garantizado utilizable.
Para las funciones virtuales no puras, el compilador puede invocar la definición más especializada de la función conocida para la clase más derivada en la jerarquía que ya ha sido construida y aún no ha sido destruida. Pero las funciones virtuales puras por definición son aquellas en las que aún no se ha especificado ninguna implementación, y en el nivel de la jerarquía de clases donde las funciones son puras, no existe una implementación para llamar.
Una implementación típica de un mecanismo de despacho virtual puede representar esto al tener un puntero en una tabla de despacho virtual para la clase base que contiene una función virtual pura que se establece en 0, sin inicializar, o apunta a alguna función de "levantar una alerta". A medida que las capas sucesivas de constructores de clases derivadas entran en juego, el puntero a la tabla de despacho virtual se sobrescribirá con la dirección de sus propios VDT. Esas clases derivadas que anulan la implementación apuntarán a su propia definición de función, que se convertirá en la predeterminada para cualquier clase derivada que no especifique una nueva implementación. Este miembro crucial del puntero implícito a VDT se desplazará hacia atrás a través de esta misma lista de VDT a medida que se completen los destructores de clase derivados, asegurando que cualquier llamada virtual sea a funciones en capas no estructuradas en la jerarquía de clases. Pero, cuando se ha ejecutado el destructor para la primera clase en la que se definió la función virtual, las futuras VDT volverán a carecer de toda referencia a una implementación real.
El estándar C ++ dice que está prohibido invocar una función virtual pura desde un constructor o destructor. ¿Cuál es la razón para esto? ¿Por qué debería el estándar colocar una restricción como esta?
En el momento en que se ejecuta un destructor de clase, ya se han ejecutado todos los destructores de subclase. No sería válido llamar a un método virtual definido por una subclase, para la cual su destructor ya se ha ejecutado.
Existe una restricción similar acerca de llamar a los métodos virtuales en los constructores. No puede llamar a un método virtual para una subclase cuyo constructor aún no se haya ejecutado.
Es la misma razón por la que no puedes vivir en una casa mientras derramas la base o la rompes. Hasta que el constructor se haya completado, el objeto solo se construye parcialmente. Y una vez que se inicia el destructor, el objeto se destruye parcialmente. Una función virtual pura solo se puede invocar en un objeto que se encuentre en un estado normal, ya que de lo contrario las estructuras necesitarían averiguar qué función para llamar a la implementación podría no existir.
Recuerde que invocar una función virtual "no pura" desde un constructor / destructor ignora el hecho de que la función es virtual y siempre llama a la implementación en su clase, no en la clase derivada que se está construyendo. Es por eso que no se puede llamar virtual puro desde constructor o destructor: en lo que a ellos respecta, su función virtual pura no tiene implementación.
la función es solo un prototipo a implementar en subclases, en realidad no existe en la clase ... por lo que no se puede invocar ni en constructor ni en destructor).
no hay implementación de la función así que, simplemente, no hay código para llamar :)
las subclases que implementan el virtual puro no existen cuando se llama al constructor / destructor.