2009-11-20 16 views
5

Microsoft Visual Studio 2008 mi sta dando il seguente avviso:Perdite di memoria di tipo incompleto?

avvertimento C4150: l'eliminazione di puntatore al tipo incompleto 'GLCM :: Component'; nessun distruttore chiamato

Questo probabilmente perché ho definito Gestori per inoltrare i tipi dichiarati in diversi punti, quindi ora la classe Handle sta richiedendo che non chiamerà il distruttore sull'oggetto specificato.

Ho VLD in esecuzione e non vedo perdite. Non sta letteralmente chiamando il distruttore per questo oggetto o è un avvertimento "non si può chiamare distruttore per oggetto"?

Ancora un'altra domanda di perdita di memoria da parte mia, haha.

+3

"È letteralmente non chiamare il distruttore per l'oggetto o si tratta di un 'non può chiamare distruttore per oggetto' avvertimento?" - Puoi testarlo in circa 15 secondi aggiungendo una dichiarazione di stampa al distruttore. –

+0

Haha, questo è un buon punto. Grazie. – Reggie

+0

Oppure inserire un punto di interruzione nel distruttore. – gdunbar

risposta

6

Spesso avviene quando si utilizza Pimpl, quindi mi concentrerò sulla soluzione c'è:

class FooImpl; 

class Foo 
{ 
public: 
    // stuff 
private: 
    Pimpl<FooImpl> m_impl; 
}; 

Il problema qui è che se non si dichiara un distruttore, verrà generato automaticamente, in linea, dal compilatore . Ma ovviamente, il compilatore non avrà idea del tipo completo di FooImpl lì.

È quindi necessario definire esplicitamente il distruttore, anche se vuoto, e inserire la definizione in un punto in cui è visibile il tipo completo di FooImpl.

// cpp file 
class FooImpl 
{ 
}; 

Foo::~Foo() {} // Empty, but now correctly generated 
       // because FooImpl complete at this point. 

Inoltre, se come me avete definito la vostra classe Pimpl essere abbastanza intelligenti (per quanto riguarda la costruzione, copia e assegnazione), poi quelli sarà anche bisogno di essere definita nel file cpp.

E 'davvero una seccatura, ma poi si sono ben incapsulato i tuoi dettagli di implementazione, quindi suppongo ne vale la pena.

0

Sembra che tu non stia seguendo lo p-implidiom correttamente.

Breve esempio:

// header 
struct Other; // declare to use a pointer to it 

struct Handle { 
    Handle(); 
    ~Handle(); 

    void f(); 

private: 
    Other* _obj; 
}; 

// implementation (.cpp) 
#include "header" 

struct Other { 
    void f(); 
}; 

Handle() : _obj(new Other()) {} 
~Handle() { delete _obj; } 

void Handle::f() { _obj->f(); } 

Dato che l'utilizzo delete è ora, dopo la definizione di classe di altri, il tipo sarà completa. Senza un tipo completo, il compilatore non può sapere come distruggerlo correttamente. (Ad esempio il dtor potrebbe essere virtuale o non virtuale, o addirittura potrebbe essere non pubbliche.)

+0

Sto utilizzando una classe maniglia template, in modo significa che non avrei mai avanti dichiarare il tipo passato per quel manico? – Reggie

+0

Ah, vedi, questa informazione importante è rimasta fuori questione. :) Sarà necessario il tipo di essere completo in quel punto (e non farà p-impl), a meno che non si vuole imparare la differenza tra modello di istanza e specializzazione, tra gli altri angoli. –

+0

Vuoi p-impl? Se è così, usa un modello diverso da un modello. In caso contrario, e non è possibile fornire un tipo completo (la definizione di classe di Altro nel mio esempio), quale ruolo viene gestito da Handle? –

6

Per l'++ standard C (ISO/IEC 14882: 2003 5.3.5/5):

Se l'oggetto da eliminare ha un tipo di classe incompleto al punto di eliminazione e la classe completa ha un distruttore non banale o una funzione di deallocazione, il comportamento non è definito.

Quindi, se la classe ha un distruttore non banale, non farlo, a prescindere da come Visual C++ gestisce questa situazione.

1

In g ++, l'avviso potrebbe essere riprodotta anche utilizzando seguente codice:

class A; 

void func(A *p) 
{ 
     delete p; 
} 

int main(int argc, char **argv) 
{ 
     return 0; 
} 

Avvertenze ma non tutti gli errori:

test.cpp: In function void func(A*): 
test.cpp:6: warning: possible problem detected in invocation 
    of delete operator: 
test.cpp:4: warning: A has incomplete type 
test.cpp:2: warning: forward declaration of struct A 
test.cpp:6: note: neither the destructor nor the class-specific 
    operator delete will be called, even if they are declared 
    when the class is defined. 

Mentre g ++ dichiara molto chiaro qui che distruttore non sarà chiamato perché non sa se ha bisogno di un distruttore o no, o dov'è il distruttore.

In questo caso delete() downgrates alla C call free(), cioè, libera solo la memoria dell'oggetto stessa, ma tutti i dati heap allocati dall'oggetto stesso internamente (ad esempio, nel costruttore) verranno mantenuti, lo stesso comportamento di free (p).

Penso che sarebbe un modo simile se in MS VC/++; e un modo corretto è includere le intestazioni di interfaccia di A.

1

Potrebbe non esserci perdite anche se il distruttore non viene chiamato se l'oggetto da eliminare non contiene puntatori agli oggetti allocati su heap. Comunque non provare questo - come James McNellis menziona nel his answer è un comportamento indefinito secondo lo standard C++.

Quando elimina viene chiamato (esplicitamente o da un puntatore intelligente) prima viene eseguito il distruttore di oggetti e quindi viene deallocata la memoria.VC++ fondamentalmente ti dice che non ha idea di cosa debba essere eseguito dal distruttore e quindi non ne eseguirà alcuna, ma invece assegnerà la memoria. Se l'oggetto è un tipo di POD o non ha puntatori fissati agli oggetti heap-assegnati (sia non contiene puntatori o contiene puntatori impostato su NULL o oggetti di proprietà di qualcun altro e quindi non necessario per rilasciare) non c'è alcun motivo per una perdita.

Quindi, forse VC++ produrrà lo stesso comportamento come con il primo lancio dell'oggetto su void*. Ma ancora una volta non fare affidamento su questo - nel migliore dei casi si avrà codice non importabile, nel peggiore dei casi il codice si romperà non appena si cambia versione o patch del compilatore VC++.