2010-09-29 2 views
16

Durante la lettura delle risposte a this question ho notato che le risposte (this per esempio) implicano che operator delete può essere chiamato anche quando l'istruzione delete viene eseguita su un puntatore nullo.Perché "operator delete" viene richiamato quando chiamo "delete" su un puntatore nullo?

così ho scritto un piccolo frammento:

class Test { 
public: 
    void* operator new(size_t) { /*doesn't matter*/ return 0; } 
    void operator delete(void* ptr) { 
     ptr; //to suppress warning and have a line to put breakpoint on 
    } 
}; 

int main() 
{ 
    Test* ptr = 0; 
    delete ptr; 
} 

e - sorprendentemente per me - Test::operator delete() viene richiamato con ptr in possesso di un puntatore nullo.

Come ho capito, operator new alloca la memoria e operator delete restituisce la memoria all'allocatore. Se chiamo l'istruzione delete su un puntatore nullo significa che non c'era alcun oggetto dietro il puntatore e che non c'è memoria per tornare all'allocatore.

delete include il richiamo di un distruttore. Quando passo un puntatore nullo il distruttore non è sicuramente invocato - C++ si prende cura di questo. Allora perché è invocato lo operator delete in questo caso?

+0

Chiedere anche perché viene richiamato 'operator new' quando si assegna un array di lunghezza zero:' new Test [0]; '...;) – ybungalobill

+1

@ybungalobill: È più semplice: lo standard richiede che il puntatore restituito sia valido e distinto . – sharptooth

+0

Si noti che se si rende virtuale il distruttore, l'overload 'operator delete' non verrà chiamato. Le implementazioni solitamente invocano la funzione direttamente dal distruttore e chiamano il distruttore senza un controllo per null (risparmiando così poche istruzioni). Solo quando la chiamata ha bisogno di invio virtuale è il controllo eseguito in anticipo. – avakar

risposta

18

La lingua del futuro standard C++ 0x (sezione 5.3.5 [expr.delete]) è la seguente:

Se il valore dell'operando di l'espressione di eliminazione non è un valore di puntatore nullo, l'espressione di eliminazione chiamerà una funzione di deallocazione (3.7.4.2). Altrimenti, è non specificato se verrà chiamata la funzione di deallocazione . [Nota: la funzione di deallocazione si chiama indipendentemente dal fatto che il distruttore per l'oggetto o qualche elemento dell'array generi un'eccezione. - nota end]

Così è un comportamento non specificato, alcuni compilatori può chiamare operator delete quando un puntatore NULL viene eliminato e altri non possono.

MODIFICA: il termine funzione di deallocation utilizzato dallo standard sembra causare confusione. Viene fornito con un riferimento. Alcuni lingua chiave dal 3.7.4.2 [basic.stc.dynamic.deallocation] che può aiutare a chiarire:

Se una classe T ha una funzione membro deallocazione denominata operator delete esattamente un parametro, quindi che la funzione è una normale (non-placement) Funzione deallocazione.

Lo standard è anche molto chiaro che definiti dall'utente operator delete necessità di accettare un parametro che rappresenta un valore di puntatore nullo:

Il valore del primo argomento fornito a una funzione deallocazione può essere valore del puntatore nullo; in tal caso, e se la funzione di deallocazione è fornita nella libreria standard, la chiamata non ha alcun effetto.

Ma a causa del comportamento non specificato 5.3.5, non si dovrebbe fare affidamento sul vostro operator delete essere chiamato quando il puntatore è nullo.

+0

Hmm, questo frammento parla della funzione di deallocazione, non dell'operatore. –

+0

C++ 03 sembra essere vago sulla questione. Dice "se il valore dell'operando di eliminazione è il puntatore nullo l'operazione non ha alcun effetto", quindi "Se l'espressione di eliminazione chiama la funzione di deallocazione dell'implementazione ...", quindi, "L'espressione di eliminazione sarà chiama una funzione di deallocazione ". Quindi o non deve chiamare l'operatore sovraccarico, o non è specificato, o deve chiamarlo. Uno dei tre ;-) –

+0

@Hans: la funzione deallocazione è la funzione che implementa 'operator delete'. Questa terminologia deriva dal fatto che gli operatori compaiono nelle espressioni, mentre la funzione che li implementa * è * la funzione. In altre parole, devi separare in qualche modo la tua terminologia. – ybungalobill

10

L'eliminazione dell'operatore è come qualsiasi altro operatore, perché non dovrebbe essere invocato? Non può esaminare i suoi argomenti prima che venga invocato il.

Questo è come chiedere perché operator+ è invocato quando si aggiunge 0.

+10

L'istruzione 'delete' non chiama semplicemente' operator delete', chiama prima il distruttore, e un controllo nullo viene necessariamente fatto prima di chiamare il distruttore (e quindi prima di chiamare 'operator delete'). 'operator delete' non è come gli altri overload dell'operatore. –

+0

Ok, ma non c'è modo per il compilatore di essere sicuro che l'eliminazione di un operatore definito dall'utente sia un no-op quando viene fornito un puntatore nullo. Potrebbe, ad esempio, registrare qualcosa che indica che hai tentato di eliminare un puntatore nullo. –

+2

D'accordo con Ben Voigt: quando si digita 'delete p;', il compilatore prima chiama il distruttore dell'oggetto e quindi la funzione deallocation 'operator delete'. Il sistema deve essere in grado di diagnosticare che il puntatore è 0 per evitare di chiamare il distruttore (che non chiamerà), quindi tecnicamente potrebbe anche evitare di chiamare il deallocator. –