2010-08-02 4 views
8

Mi sono imbattuto nel mio primo compilatore che cambia il lvalue passato a :: delete, ma non azzera il lvalue. Questo è quanto segue:Dove nel C++ Standard si dice :: delete può cambiare lvalue?

Foo * p = new Foo(); 
Foo * q = p; 
assert(p != 0); 
assert(p == q); 
::delete p; 
assert(p != q); 
assert(p != 0); 

Si noti che p non è zero dopo l'operazione di cancellazione e che è stato modificato dal vecchio valore. Un collega mi ha detto che questo non è insolito nella sua esperienza avendo lavorato con alcuni compilatori C++ mainframe che avrebbero cambiato p in 0xFFFFFFFF, così come altri compilatori che avrebbero cambiato p a 0.

Dove nello standard C++ dice che un compilatore è autorizzato a fare questo?

La ricerca attraverso StackOverflow, ho trovato questa domanda: Why doesn’t delete set the pointer to NULL? che ha avuto una risposta che di cui Bjarne Stroustrup's response che include l'istruzione:

C++ consente esplicitamente un'implementazione di cancellazione per azzerare un operando lvalue, e ho avuto sperato che le implementazioni lo farebbero, ma quell'idea non sembra essere diventata popolare con gli implementatori.

Ho letto e riletto la sezione 5.3.5 e 12.5 di final committee draft C++0x standard, ma non vedo la parte "esplicita". Sto solo guardando nella parte sbagliata dello standard? O c'è una catena di logica che si trova nelle sezioni, ma io non sto semplicemente collegando correttamente.

Non ho più la mia copia del Manuale di riferimento Annotated C++. Era nel ARM che un compilatore poteva fare questo?

[Modifica: correzione del riferimento di sezione da 3.5.3 a 5.3.5. Aggiungo anche un interessante paradosso come contrappunto all'affermazione di Henk secondo cui p non è definito dopo l'eliminazione.]

C'è un paradosso interessante se p è inizializzato su null.

Foo * p = 0; 
Foo * q = p; 
assert(p == 0); 
assert(p == q); 
::delete p; 
assert(p == q); 
assert(p == 0); 

In questo caso, tuttavia, il comportamento è ben documentato. Quando delete ottiene un puntatore nullo, si suppone di non fare nulla, quindi p rimane invariato.

+0

Ebbene, il braccio è né qui né là. E dimentichiamoci di lvalues. La tua domanda "Può cancellare cambiare argomento?". –

+0

Inoltre, lo standard non è grande nel dire cosa è permesso a un compilatore di fare - specifica solo ciò che deve e non deve fare. Non riesco a vedere nulla nello standard corrente che impedisca la modifica del valore del puntatore. –

+1

Non ho espresso la mia domanda come "Può cancellare cambiare argomento?" perché non volevo entrare in una classe che dichiara che l'operatore cancella il supporto cambiando la sua argomentazione dal momento che prende solo void *, non un void * & or void **. – Ants

risposta

8

Potrebbe non essere così esplicito. In 5.3.5/7 dice che l'espressione delete chiamerà una funzione deallocator. Quindi in 3.7.3.2/4 si dice che l'utilizzo di un puntatore che è stato deallocato non è definito. Poiché il valore del puntatore non può essere utilizzato dopo la deallocazione, se il puntatore mantiene il valore o il valore viene modificato dall'implementazione non fa alcuna differenza.

5.3.5/7

L'eliminazione espressione chiamerà una funzione deallocazione (3.7.3.2).

3.7.3.2/4

Se l'argomento fornito a una funzione deallocazione nella libreria standard è un puntatore che non è il valore puntatore nullo (4.10), la funzione deallocazione deve deallocare la memorizzazione referenziato dal puntatore, rendendo non validi tutti i puntatori che fanno riferimento a qualsiasi parte dell'archivio deallocato. L'effetto dell'utilizzo di un valore di puntatore non valido (incluso il passaggio a una funzione di deallocazione) non è definito.

I riferimenti sono dallo standard corrente. Nello standard imminente 5.3.5/7 è stato riformulato:

C++ 0x FD 5.3.5/7

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

+0

OK, prendo questo come supporto per il mio punto. Se l'utilizzo di un puntatore non valido non è definito, un compilatore intelligente è libero di modificare i puntatori che sa essere non valido, in modo che gli errori vengano visualizzati meglio. Fallire velocemente, fallire presto –

+2

@Henk: Sono completamente d'accordo con l'intento del compilatore di supportare la filosofia "fail fast, fail early". Ha esposto un bug nel codice che è stato spedito in quasi 10 anni. Abbiamo un'implementazione di lista doppiamente collegata che aggiorna automaticamente i puntatori del prossimo, precedente e primo nodo nel distruttore di un nodo. Il distruttore della lista è stato codificato come "while (m_firstNode) delete m_firstNode;" Il distruttore del nodo aggiorna correttamente m_firstNode, ma il compilatore sovrascrive m_firstNode dopo la chiamata del distruttore con un valore diverso da zero. Kaboom! Se fosse esplicito, il codice originale sarebbe stato diverso. – Ants

+0

@Ants: interessante. Questo lo porta al limite, aggiungendo alias al mix. –