Llamar a la función virtual desde el destructor

¿Esto es seguro?

class Derived: public PublicBase, private PrivateBase { ... ~Derived() { FunctionCall(); } virtual void FunctionCall() { PrivateBase::FunctionCall(); } } class PublicBase { virtual ~PublicBase(){}; virtual void FunctionCall() = 0; } class PrivateBase { virtual ~PrivateBase(){}; virtual void FunctionCall() { .... } } PublicBase* ptrBase = new Derived(); delete ptrBase; 

Este código se copia a veces con IP en una dirección incorrecta.

Esa no es una buena idea para llamar a una función virtual en el constructor. Está claro para todos.

De artículos como http://www.artima.com/cppsource/nevercall.html Entiendo que destructor también es un lugar no tan bueno para llamar a una función virtual.

Mi pregunta es “¿Es esto cierto?” He probado con VS2010 y VS2005 y se llama a PrivateBase :: FunctionCall. ¿Es un comportamiento indefinido?

Voy a ir contra stream aquí … pero primero, debo asumir que su destructor de PublicBase es virtual, ya que de lo contrario el destructor Derived nunca será llamado.

Por lo general, no es una buena idea llamar a una función virtual desde un constructor / destructor

La razón de esto es que el envío dynamic es extraño durante estas dos operaciones. El tipo real del objeto cambia durante la construcción y cambia de nuevo durante la destrucción. Cuando se está ejecutando un destructor, el objeto es exactamente de ese tipo, y nunca un tipo derivado de él. El envío dynamic está vigente en todo momento, pero el rebase final de la función virtual cambiará dependiendo de la jerarquía en la que se encuentre.

Es decir, nunca se debe esperar que una llamada a una función virtual en un constructor / destructor se ejecute en cualquier tipo derivado del tipo del constructor / destructor que se está ejecutando.

Pero

En su caso particular, el exceso de control final (al menos para esta parte de la jerarquía) está por encima de su nivel. Además, no está utilizando el envío dynamic en absoluto. La llamada PrivateBase::FunctionCall(); está estáticamente resuelto, y es efectivamente equivalente a una llamada a cualquier función no virtual. El hecho de que la función sea virtual o no no afecta esta llamada.

Así que , está bien hacerlo mientras lo hace, aunque se verá obligado a explicar esto en las revisiones de código ya que la mayoría de las personas aprenden el mantra de la regla en lugar de la razón para ello.

¿Esto es seguro?

Sí. Llamar a una función virtual desde un constructor o destructor distribuye la función como si el tipo dynamic del objeto fuera el que está siendo construido o destruido actualmente. En este caso, se llama desde el destructor de Derived , por lo que se envía a Derived::FunctionCall (que, en su caso, llama a PrivateBase::FunctionCall no virtual). Todo esto está bien definido.

No es una “buena idea” invocar funciones virtuales desde un constructor o destructor por tres razones:

  • Causará un comportamiento inesperado si lo llama desde una clase base y (erróneamente) espera que se envíe a una anulación en una clase derivada;
  • Causará un comportamiento indefinido si es puramente virtual;
  • Tendrás que explicar tu decisión a las personas que creen que siempre está mal.

En general, no es una buena idea llamar a una función virtual, a menos que el objeto de la clase al que pueda enviarse (es decir, el objeto “completo” de la clase más derivada) esté completamente construido. Y este no es el caso

  • hasta que todos los constructores terminen la ejecución
  • después de que cualquier destructor termine la ejecución

Es una muy mala idea según scott: enlace

Esto es lo que he comstackdo y ejecutado para ayudarme a obtener una mejor comprensión del proceso de destrucción, también podría ser útil.

 #include  using namespace std; class A { public: virtual void method() { cout < < "A::method" << endl; } void otherMethod() { method(); } virtual ~A() { cout << "A::destructor" << endl; otherMethod(); } }; class B : public A { public: virtual void method() { cout << "B::method" << endl; } virtual ~B() { cout << "B::destructor" << endl; } }; int main() { A* a = new B(); a->method(); delete a; }