puro polimorfismo multiples herencias herencia ejercicios doble derivadas derivada clases clase c++ c++11 virtual-destructor

polimorfismo - Destructor virtual con miembros virtuales en C++ 11



multiples herencias c++ (3)

Como autor de las diapositivas trataré de aclarar.

Si escribe código asignando explícitamente una instancia Derived con una new y destruyéndola con delete usando un puntero de clase base, entonces necesita definir un destructor virtual , de lo contrario terminará destruyendo de forma incompleta la instancia Derived . Sin embargo, recomiendo abstenerse de new y delete completo y usar exclusivamente shared_ptr para referirse a objetos polimórficos asignados a shared_ptr , como

shared_ptr<Base> pb=make_shared<Derived>();

De esta manera, el puntero compartido realiza un seguimiento del destructor original que se utilizará, incluso si se shared_ptr<Base> para representarlo. Una vez, el último shared_ptr referente se sale del alcance o se restablece, se llamará a ~Derived() y se liberará la memoria. Por lo tanto, no es necesario que haga ~Base() virtual.

unique_ptr<Base> y make_unique<Derived> no proporcionan esta característica, porque no proporcionan la mecánica de shared_ptr con respecto al eliminador , porque el puntero único es mucho más simple y apunta a la sobrecarga más baja y por lo tanto no almacena el extra Puntero de función necesario para el borrado. Con unique_ptr la función deleter es parte del tipo y, por lo tanto, uniqe_ptr con un deleter referente a ~Derived no sería compatible con un unique_ptr<Base> usando el deleter predeterminado, lo que sería incorrecto para una instancia derivada de todos modos, si ~Base no estuviera No es virtual.

Las sugerencias individuales que hago, están destinadas a ser fáciles de seguir y seguir todas juntas. Intentan producir un código más simple, al permitir que los componentes de la biblioteca y el código generado por el compilador hagan toda la administración de recursos.

La definición de un destructor (virtual) en una clase, prohibirá un operador de asignación / asignación de movimiento proporcionado por el compilador y podría prohibir también que un operador de construcción / asignación de copia provisto por el compilador en versiones futuras de C ++. Resurrirlos se ha vuelto fácil con =default , pero aún parece un montón de código repetitivo. Y el mejor código es el código que no tiene que escribir, porque no puede estar equivocado (sé que todavía hay excepciones a esa regla).

Para resumir "No defina un destructor (virtual)" como corolario de mi "Regla de cero":

Siempre que diseñe una jerarquía de clases polimórficas (OO) en C ++ moderno y desee / necesite asignar sus instancias en el montón y acceder a ellas mediante un puntero de clase base, use make_shared<Derived>() para crear instancias y shared_ptr<Base> para mantenerlas alrededor. Esto te permite mantener la "Regla de cero".

Esto no significa que deba asignar todos los objetos polimórficos en el montón. Por ejemplo, la definición de una función que toma un (Base&) como parámetro, se puede llamar con una variable Derived local sin problemas y se comportará polimórfica, con respecto a las funciones de miembros virtuales de Base .

En mi opinión, el polimorfismo OO dinámico está muy usado en muchos sistemas. No debemos programar como Java, cuando usamos C ++, a menos que tengamos un problema, donde el polimorfismo dinámico con objetos asignados al montón es la solución correcta.

En estas diapositivas sobre el estándar C ++ 11/14, en la diapositiva 15, el autor escribe que "muchas reglas de codificación clásicas [ya] no son aplicables" en C ++ 11. Él propone una lista de tres ejemplos, y estoy de acuerdo con la Regla de los Tres y la gestión de la memoria.

Sin embargo, su segundo ejemplo es "destructor virtual con miembros virtuales" (solo eso). Qué significa eso? Sé que uno debe declarar como virtual el destructor de clase base para poder llamar al destructor correcto si tenemos algo como

Base *b = new Derived; ... delete b;

Esto está bien explicado aquí: ¿ Cuándo usar destructores virtuales?

¿Pero es inútil ahora en C ++ 11 declarar tu destructor virtual si tienes miembros virtuales?


Creo que esto tiene que ver con la "regla de cero" mencionada en otra parte de la presentación.

Si solo tiene variables de miembro automáticas (es decir, use shared_ptr o unique_ptr para los miembros que de otra manera serían punteros en bruto), entonces no necesita escribir sus propios constructores de copia o movimiento, u operadores de asignación: los valores predeterminados proporcionados por el compilador serán óptimos . Con la inicialización en clase, tampoco necesita un constructor predeterminado. Y, finalmente, no necesitas escribir un destructor en absoluto, virtual o no.


El papel vinculado muestra el código correspondiente:

std::unique_ptr<Derived> { new Derived };

El eliminador almacenado es std::default_delete<Derived> , que no requiere que Base::~Base sea ​​virtual.

Ahora puede mover esto a un unique_ptr<Base> , y también moverá std::default_delete<Derived> sin convertirlo en un std::default_delete<Base> .