ejecutar compiler compilar compilador c++ g++ destructor

c++ - compiler - gcc linux



GNU GCC(g++): ¿Por qué genera múltiples dtors? (2)

Generalmente hay dos variantes del constructor ( no a cargo / a cargo ) y tres del destructor ( no a cargo / a cargo / a cargo ).

El controlador y el dtor no a cargo se usan cuando se maneja un objeto de una clase que hereda de otra clase usando la palabra clave virtual , cuando el objeto no es el objeto completo (por lo que el objeto actual "no está a cargo" de construir o destruir el objeto base virtual). Este controlador recibe un puntero al objeto base virtual y lo almacena.

Los códigos y dtors a cargo son para todos los demás casos, es decir, si no hay una herencia virtual involucrada; si la clase tiene un destructor virtual, el puntero dtor eliminador de carga entra en la ranura vtable, mientras que un ámbito que conoce el tipo dinámico del objeto (es decir, para objetos con duración de almacenamiento automática o estática) utilizará el dtor a cargo (porque esta memoria no debe ser liberada).

Ejemplo de código:

struct foo { foo(int); virtual ~foo(void); int bar; }; struct baz : virtual foo { baz(void); virtual ~baz(void); }; struct quux : baz { quux(void); virtual ~quux(void); }; foo::foo(int i) { bar = i; } foo::~foo(void) { return; } baz::baz(void) : foo(1) { return; } baz::~baz(void) { return; } quux::quux(void) : foo(2), baz() { return; } quux::~quux(void) { return; } baz b1; std::auto_ptr<foo> b2(new baz); quux q1; std::auto_ptr<foo> q2(new quux);

Resultados:

  • La entrada dtor en cada uno de los cuadros para foo , baz y quux apuntan al dtor eliminador correspondiente.
  • b1 y b2 están construidos por baz() a cargo , que llama a foo(1) a cargo
  • q1 y q2 están construidos por quux() a cargo , que cae foo(2) a cargo y baz() no a cargo con un puntero al objeto foo que construyó anteriormente
  • q2 es destruido por ~auto_ptr() a cargo , que llama al virtual dtor ~quux() borrado a cargo , que llama ~baz() no a cargo , ~foo() a cargo y operator delete .
  • q1 es destruido por ~quux() a cargo , que llama ~baz() no a cargo y ~foo() a cargo
  • b2 es destruido por ~auto_ptr() a cargo , que llama al virtual dtor ~baz() borrado a cargo , que llama a ~foo() a cargo y al operator delete
  • b1 es destruido por ~baz() a cargo , que llama ~foo() a cargo

Cualquiera que derive de quux usaría su ctor y dtor no a cargo y asumiría la responsabilidad de crear el objeto foo .

En principio, la variante no a cargo nunca se necesita para una clase que no tiene bases virtuales; en ese caso, la variante a cargo se denomina a veces unificada , y / o los símbolos para ambos, a cargo y no a cargo, se alias a una única implementación.

Entorno de desarrollo: GNU GCC (g ++) 4.1.2

Mientras trato de investigar cómo aumentar la cobertura del código, particularmente la cobertura de funciones, en las pruebas unitarias, descubrí que algunas de las clases parecen generarse varias veces. ¿Alguno de ustedes tiene alguna idea sobre por qué, por favor?

Intenté y observé lo que mencioné anteriormente usando el siguiente código.

En "test.h"

class BaseClass { public: ~BaseClass(); void someMethod(); }; class DerivedClass : public BaseClass { public: virtual ~DerivedClass(); virtual void someMethod(); };

En "test.cpp"

#include <iostream> #include "test.h" BaseClass::~BaseClass() { std::cout << "BaseClass dtor invoked" << std::endl; } void BaseClass::someMethod() { std::cout << "Base class method" << std::endl; } DerivedClass::~DerivedClass() { std::cout << "DerivedClass dtor invoked" << std::endl; } void DerivedClass::someMethod() { std::cout << "Derived class method" << std::endl; } int main() { BaseClass* b_ptr = new BaseClass; b_ptr->someMethod(); delete b_ptr; }

Cuando construí el código anterior (g ++ test.cpp -o test) y luego veo qué tipo de símbolos se han generado de la siguiente manera:

nm - prueba de detección

Pude ver la siguiente salida.

==== following is partial output ==== 08048816 T DerivedClass::someMethod() 08048922 T DerivedClass::~DerivedClass() 080489aa T DerivedClass::~DerivedClass() 08048a32 T DerivedClass::~DerivedClass() 08048842 T BaseClass::someMethod() 0804886e T BaseClass::~BaseClass() 080488f6 T BaseClass::~BaseClass()

Mis preguntas son las siguientes:

1) ¿Por qué se generaron múltiples dtors (BaseClass - 2, DerivedClass - 3)?

2) ¿Cuál es la diferencia entre estos dtors? ¿Cómo se usarán esos dtors múltiples de forma selectiva?

Ahora tengo la sensación de que para lograr una cobertura 100% funcional para el proyecto C ++, tendríamos que entender esto para poder invocar todos esos dtors en mis pruebas unitarias.

Agradecería enormemente si alguien pudiera darme la respuesta sobre lo anterior.


Primero, los propósitos de estas funciones se describen en Itanium C ++ ABI ; vea las definiciones en "destructor de objetos base", "destructor de objetos completos" y "eliminación de destructor". El mapeo de nombres destrozados se da en 5.1.4.

Básicamente:

  • D2 es el "destructor de objetos base". Destruye el objeto en sí, así como los miembros de datos y las clases base no virtuales.
  • D1 es el "destructor de objetos completo". Además, destruye las clases base virtuales.
  • D0 es el "destructor de objeto eliminado". Hace todo lo que hace el destructor de objetos completo, además llama al operator delete para liberar realmente la memoria.

Si no tiene clases base virtuales, D2 y D1 son idénticos; GCC, en niveles de optimización suficientes, alias los símbolos del mismo código para ambos.