2012-01-11 8 views
10

perche il seguente codice:Cosa succede durante l'istruzione `delete this;`?

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this;} 
private: 
    int x; 
}; 

Quello che sta accadendo (ed è valido?) Nei seguenti due opzioni:

opzione 1:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
    delete a; 
} 

Opzione 2:

void main() 
{ 
    foo a; 
    a.done(); 
} 

Sarà la seconda dichiarazione delete a; a opt ion 1 causerà un'eccezione o un danneggiamento dell'heap?

L'opzione 2 causerà un'eccezione o un danneggiamento dell'heap?

+0

Hai dimenticato le parentesi di apertura della classe per errore o è esattamente il codice copiato? – Neophile

+0

@Nerds: a typo - fixed ... – NirMH

+0

Interessante. Immagino che il primo causerebbe una corruzione segfault o heap, e il secondo farà tutto ciò che elimina un puntatore allo stack. – cha0site

risposta

19

delete this; è consentito, elimina l'oggetto.

Entrambi i frammenti di codice hanno un comportamento non definito - nel primo caso l'eliminazione di un oggetto che è già stato eliminato e nel secondo caso l'eliminazione di un oggetto con durata di archiviazione automatica.

Poiché il comportamento non è definito, lo standard non indica se causerà un'eccezione o un danneggiamento dell'heap. Per diverse implementazioni potrebbe essere né, né, né entrambi, e potrebbe o meno essere uguale ogni volta che si esegue il codice.

+0

E si spera che sarà il primo. – Xeo

+1

Comportamento indefinito, la risposta migliore e corretta. +1 –

4

Entrambi causerebbero un errore.

Il primo puntatore viene eliminato due volte, con la seconda delete causa l'errore mentre il secondo è allocato sullo stack e non può essere soppresso implicitamente (errore causato dalla prima chiamata del distruttore).

+0

Mi hai battuto! – Neophile

+0

Ho modificato la domanda. Penso che dovrebbe essere allocato su heap e poi cancellato con 'done'. Cosa sta succedendo in questo caso? L'eliminazione nello stack non ha senso e probabilmente non viene richiesta dall'OP. – duedl0r

+0

La tua risposta è corretta, ma la domanda era quale * tipo * di errore si sarebbe verificato. – cha0site

1

Entrambi avrebbero causare un errore, ciò che si vuole è:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
} 

cui il compilatore si espanderà a qualcosa di simile al di sotto, che spero marche eliminazione di "questo" un po 'meno confusione.

void __foo_done(foo* this) 
{ 
    delete this; 
} 

void main() 
{ 
    foo* a = new foo(); 
    __foo_done(a); 
} 

Vedi anche, Is it legal (and moral) for a member function to say delete this?

+0

Cosa succede se è il primo membro di un oggetto in una classe di layout standard (o POD), che è stato allocato con plain new e che non ha altri distruttori? (Non che questo non sarebbe _insane_, ma, beh ...) – Random832

+0

@ Random832: Non riesco a dare un senso alla tua domanda, mi dispiace. – ronag

+0

La domanda FAQ che colleghi fornisce un elenco di condizioni in base alle quali "cancella questo" è sicuro. Poiché il primo membro di una classe di layout standard ha lo stesso indirizzo dell'oggetto genitore stesso, sembra che teoricamente sarebbe un altro caso. – Random832

1

Calling delete this è una cattiva idea. Chiunque chiama new deve chiamare lo delete. Da qui i problemi evidenziati nelle altre risposte.

Inoltre è possibile avere perdite di memoria/comportamento non definito durante la costruzione di una matrice di oggetti.

+0

Perché chiamare "cancella questo" è una cattiva idea? Direi che in molte (più?) Applicazioni, sarebbe la forma più comune di "cancella". –

+0

@JamesKanze Perché? 'delete this' non è normale modo di rilasciare oggetti. –

+0

@VJovic Dipende dall'applicazione.In molte applicazioni, il più frequente (o anche il solo) 'delete's sarà' delete this'. In altri, 'delete' sarà sistematicamente nel gestore delle transazioni, o qualcosa di simile. E negli altri ... Dipende dal motivo per cui stai usando la memoria dinamica. Se si tratta di oggetti del modello, allora "cancella questo" o un gestore delle transazioni sono i più frequenti. Se si tratta di strutture di dati complesse di dimensioni sconosciute, il proprietario della struttura eseguirà 'delete', e' delete this' non si verificherà quasi mai. –

0

In entrambi i casi il tuo heap verrà danneggiato. Naturalmente, non è possibile utilizzare nell'opzione 2 "->" se il valore di sinistra (in questo caso, "a") non è un puntatore, la forma corretta nell'opzione 2 sarebbe a.done(); Ma sì, dovresti ottenere un danneggiamento dell'heap se provi a eliminare 1) cosa è già stato eliminato, o 2) variabile locale.

La chiamata "elimina questo" è tecnicamente valida e libera la memoria.

EDIT ero curioso ed effettivamente provato questo:

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this; } 
    void test() { x = 2; } 
    int getx() { return x; } 
private: 
    int x; 
} ; 


int main(int argc, char* argv[]) 
{ 
    foo *a = new foo(); 
    a->done(); 
    a->test(); 
    int x = a->getx(); 
    printf("%i\n", x); 
    return x; 
} 

Una chiamata a printf riuscirà, e x terrà il valore 2.

+0

Grazie - in realtà l'utilizzo "->" nell'opzione 2 era un refuso da una modifica esterna - ho aggiornato di nuovo la domanda. – NirMH

+0

Quindi, 'delete this' rilascerà la memoria dell'oggetto dall'heap? – NirMH

+0

@NirMH: l'utilizzo "->" è stato scritto da te. – duedl0r

0

Sarei molto cauto sul contesto in cui si libera memoria. Mentre si chiama "elimina questo;" tenterà di liberare la memoria associata alla struttura della classe, tale operazione non ha modo di dedurre il contesto dell'allocazione di memoria originale, cioè, se è allocata dall'heap o dallo stack. Il mio consiglio è di rielaborare il codice in modo che l'operazione di deallocazione venga invocata da un contesto esterno. Si noti che questo è diverso dal deallocare le strutture interne alla classe, in tal caso, utilizzare il distruttore.

3

Questa domanda ha avuto risposta, ma aggiungerò un nuovo punto che se la classe chiama elimina questo, è necessario rendere privato il distruttore.

Ciò garantisce che solo la classe possa cancellare se stessa.

Se si rende privato il proprio destructor, entrambi i campioni di codice non verranno compilati.

+0

ovviamente non impedisce tutti gli errori, ad es. usando l'oggetto dopo aver chiamato done() su di esso (anche chiamando di nuovo done() invocherà una doppia eliminazione). – CashCow

Problemi correlati