2010-10-26 21 views
5

Di seguito sono riportati due frammenti (pronti per la compilazione) del codice. Nel primo frammento in cui sto usando solo la dichiarazione anticipata per una struct mentre si elimina il puntatore a questa struct da un dtor di classe Base per una classe Guest non viene invocato.
Nel secondo frammento quando invece di inoltro di dichiarazione utilizzo la definizione completa di questa classe Guest utilizzando delete in Base funziona come previsto.
Perché? Perché fa la differenza? La dichiarazione anticipata non è forse solo una nota per un compilatore che dice che la definizione di questa classe/struct è altrove?
Sono molto sorpreso che non funzioni in modo intuitivo.La dichiarazione di inoltro non funziona appena

//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; 
} 

// In secondo luogo - piena def

#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

È consigliabile rendere virtuali i costruttori della classe base in entrambi i casi. –

+0

@ Space_C0wb0y: Gentile, non è possibile creare un costruttore virtual in C++ :) –

+0

@Armen: Words ... smoke and mirrors. Era così chiaro nella mia mente. (Per chi si meraviglia, dovrebbe essere * distruttori *) –

risposta

3

Informalmente: il compilatore ha bisogno della definizione della classe per eliminare correttamente l'oggetto, poiché è necessario sapere come chiamare il distruttore e/o operator delete per quella classe.

Formalmente, 5.3.5/5:

Se l'oggetto eliminato ha tipo classe incompleta al punto di delezione e la classe completa ha un distruttore non banale o una funzione deallocazione , il comportamento è indefinito.

Sarebbe OK se (ad esempio) Guest era POD, ma hai dato un distruttore, quindi non sei OK.

3

Non è possibile cancellare l'ospite se non si conosce la sua definizione. Il distruttore non sarà chiamato. Inoltre, se Guest ha definito l'eliminazione di un operatore personalizzato, verrebbe ignorato.

+0

-> definizione :) –

+0

No, una dichiarazione è sufficiente. Tuttavia, una dichiarazione anticipata non lo farà. –

+0

@kotlinski - 'classe X {void f(); }; 'questa è la definizione di X mentre' classe X; 'è la dichiarazione –

16

Dalla C++ standard (5.3.5/5):

Se l'oggetto eliminato ha tipo classe incompleta al punto di delezione e la classe completa ha un distruttore non banale o una funzione deallocazione , il comportamento non è definito.

Quindi non è possibile utilizzare Elimina sul tipo incompleto. Chiamerebbe il distruttore e il compilatore non ne è ancora al corrente.

3

Non è possibile eliminare un puntatore a un tipo incompleto. Elimina è una delle operazioni che richiede il completamento del tipo. HTH

2

Il tipo di ptr_ è incompleto quando si chiama delete su di esso. Questo porta a comportamenti non definiti. Quindi il tuo distruttore non può essere chiamato. È possibile utilizzare Boost.checked_delete per evitare tali scenari.

2

(. L'intestazione stdafx.h non è standard C++) Se compilo con g ++, il compilatore 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. 

Configurare il compilatore per compilare in adeguati livelli di avviso e di errore.

+1

int _tmain (int argc, _TCHAR * argv []) <- anche questo non è standard –

Problemi correlati