2010-10-26 22 views
5

A continuación se encuentran dos fragmentos (listos para compilar) del código. En el primer fragmento en el que estoy usando solo la declaración forward para una estructura mientras elimino el puntero a esta estructura de un dtor de clase Base para una clase Guest no se invoca.
En el segundo fragmento, cuando en lugar de la declaración directa utilizo la definición completa de esta clase de Invitado utilizando delete en Base funciona como se pretendía.
¿Por qué? ¿Por qué hace la diferencia? ¿No se supone que la declaración directa es solo una nota para un compilador que dice que la definición de esta clase/estructura está en otro lugar?
Estoy muy sorprendido de que simplemente no funcione de manera intuitiva.La declaración directa no funcionará

//First just forward dclr 
#include "stdafx.h" 
#include <iostream> 
using std::cout; 

struct Guest; 

struct Base 
{ 
    Guest* ptr_; 
    Base(Guest* ptr):ptr_(ptr) 
    { 
     cout << "Base\n"; 
    } 
    ~Base() 
    { 
     cout << "~Base\n"; 
     delete ptr_; 
    } 
}; 

struct Guest 
{ 
    Guest() 
    { 
     cout << "Guest\n"; 
     throw std::exception(); 
    } 
    Guest(int) 
    { 
     cout << "Guest(int)\n"; 
    } 
    ~Guest() 
    { 
     cout << "~Guest\n"; 
    } 
}; 

struct MyClass : Base 
{ 
    Guest g; 
    MyClass(Guest* g):Base(g) 
    { 
     cout << "MyClass\n"; 

    } 
    ~MyClass() 
    { 
     cout << "~MyClass\n"; 
    } 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    try 
    { 
     Guest* g = new Guest(1); 
    MyClass mc(g); 
    } 
    catch(const std::exception& e) 
    { 
     std::cerr << e.what(); 
    } 
    return 0; 
} 

// Segundo - def completa

#include "stdafx.h" 
#include <iostream> 
using std::cout; 

struct Guest 
{ 
    Guest() 
    { 
     cout << "Guest\n"; 
     throw std::exception(); 
    } 
    Guest(int) 
    { 
     cout << "Guest(int)\n"; 
    } 
    ~Guest() 
    { 
     cout << "~Guest\n"; 
    } 
}; 

struct Base 
{ 
    Guest* ptr_; 
    Base(Guest* ptr):ptr_(ptr) 
    { 
     cout << "Base\n"; 
    } 
    ~Base() 
    { 
     cout << "~Base\n"; 
     delete ptr_; 
    } 
}; 



struct MyClass : Base 
{ 
    Guest g; 
    MyClass(Guest* g):Base(g) 
    { 
     cout << "MyClass\n"; 

    } 
    ~MyClass() 
    { 
     cout << "~MyClass\n"; 
    } 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    try 
    { 
     Guest* g = new Guest(1); 
    MyClass mc(g); 
    } 
    catch(const std::exception& e) 
    { 
     std::cerr << e.what(); 
    } 
    return 0; 
} 
+0

Debe hacer que sus constructores de clase base sean virtuales en ambos casos. –

+0

@ Space_C0wb0y: Estimado, no puedes hacer un constructor virtual en C++ :) –

+0

@Armen: Palabras ... humo y espejos. Estaba tan claro en mi mente. (Para aquellos que se preguntan, debería ser * destructores *) –

Respuesta

3

Informalmente: el compilador necesita la definición de clase para eliminar el objeto correctamente, porque necesita saber cómo llamar al destructor y/o operator delete para esa clase.

Formalmente, 5.3.5/5:

Si el objeto que está siendo borrada tiene tipo de clase incompleta en el punto de deleción y la clase completa tiene un destructor no trivial o una función desasignación , el comportamiento es indefinido.

Estaría bien si (por ejemplo) Guest fuera POD, pero le dio un destructor, por lo que no está bien.

3

No se puede eliminar el invitado a menos que sepa su definición. No se llamará al destructor. Además, si Guest ha definido una eliminación de operador personalizado, se ignoraría.

+0

declaration -> definition :) –

+0

No, una declaración es suficiente. Sin embargo, una declaración directa no funcionará. –

+0

@kotlinski - 'clase X {void f(); }; 'esta es la definición de X mientras que' clase X; 'es la declaración –

16

Desde el C++ estándar (5.3.5/5):

Si se elimina el objeto tiene tipo de clase incompleta en el punto de deleción y la clase completa tiene un destructor no trivial o una función de cancelación de asignación , el comportamiento no está definido.

Así que no puede usar la eliminación en su tipo incompleto. Llamaría al destructor y el compilador todavía no lo sabe.

3

No se puede eliminar un puntero a un tipo incompleto. Eliminar es una de las operaciones que requiere que el tipo esté completo. HTH

2

El tipo de ptr_ está incompleto al invocar delete en él. Esto conduce a un comportamiento indefinido. Entonces su destructor no puede ser llamado. Puede usar Boost.checked_delete para evitar tales situaciones.

2

(. El encabezado stdafx.h no es C++ estándar) Si compila con g ++, el compilador genera:

warning: possible problem detected in invocation of delete operator: 
warning: invalid use of incomplete type ‘struct Guest’ 
warning: forward declaration of ‘struct Guest’ 
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined. 

Configurar el compilador para compilar en niveles apropiados de advertencia y error.

+1

int _tmain (int argc, _TCHAR * argv []) <- esto tampoco es estándar –

Cuestiones relacionadas