2009-02-12 15 views
5
#define SAFE_DELETE(a) if((a) != NULL) delete (a); (a) = NULL; 

OQuale versione di safe_delete è migliore?

template<typename T> void safe_delete(T*& a) { 
    delete a; 
    a = NULL; 
} 

o qualsiasi altro modo migliore

+3

è ancora stupido, ma la cancellazione sicura delle macro dovrebbe essere: #define SAFE_DELETE (a) do {delete (a); (a) = NULL; } while (0) –

risposta

5

Chiaramente la funzione, per una semplice ragione. La macro valuta il suo argomento più volte. Questo può avere effetti collaterali malefici. Anche la funzione può essere circoscritta. Niente di meglio di quello :)

5

Generalmente, preferiscono funzioni inline sopra macro come macro non rispettano portata, e possono essere in conflitto con alcuni simboli durante preelaborazione, portando a molto strani errori di compilazione.

Ovviamente a volte i modelli e le funzioni non funzionano, ma qui non è così.

Inoltre, la cancellazione sicura non è necessaria, poiché è possibile utilizzare puntatori intelligenti, pertanto non è necessario ricordare di utilizzare questo metodo nel codice client, ma incapsularlo.

(modifica) Come altri hanno sottolineato, cassetta di sicurezza-delete non è sicuro, come anche se qualcuno fa non dimenticare di utilizzarlo, ancora non può avere l'effetto desiderato. Quindi in realtà è completamente inutile, perché l'uso di safe_delete ha bisogno di più pensieri che solo l'impostazione di 0 da soli.

7

eliminare a;

ISO C++ specifica che l'eliminazione su un puntatore NULL non fa nulla.

Citazione di iso 14882:

 
    5.3.5 Delete [expr.delete] 

    2 [...] In either alternative, if the value of the operand of delete is the 
     null pointer the operation has no effect. [...] 

saluti, Bodo

/edit: Non ho notato l'a = NULL; nel post originale, quindi nuova versione: elimina a; a = NULL; tuttavia, il problema con l'impostazione di a = NULL è già stato segnalato (falsa sensazione di sicurezza).

+0

Informazioni sul test per NULL e prestazioni: so che l'eliminazione potrebbe essere chiamata con NULL, ma if (p! = NULL) è un'operazione di lettura rapida che potrebbe saltare una chiamata di funzione e un assegnamento (scrivere a memoria). Rimuovere il se non è sbagliato, ma che dire dei programmatori junior che lo leggono e delle prestazioni? –

+0

Vero, d'altra parte: il test per NULL è un salto condizionale. Se scrivi il tuo codice in un modo, che normalmente non si liberano i puntatori NULL, basta duplicare quel salto condizionato, perché lo fai tu stesso e poi chiama comunque una funzione, che lo fa di nuovo. Quindi, a mio avviso, è meglio insegnare a fare il corretto monitoraggio delle risorse che a fare test NULL. –

2

Non è necessario verificare la nullità con delete, è equivalente a un non operativo. (a) = NULL mi fa sollevare un sopracciglio. La seconda opzione è migliore.

Tuttavia, se si dispone di una scelta, è necessario utilizzare i puntatori intelligenti, ad esempio std::auto_ptr o tr1::shared_ptr, che già fanno questo per voi.

+0

Né auto_ptr né shared_ptr si imposta su NULL alla cancellazione. QPointer è l'unica classe che conosco che lo fa. –

19

Direi nessuno dei due, poiché entrambi vi daranno un falso senso di sicurezza. Ad esempio, si supponga di avere una funzione:

void Func(SomePtr * p) { 
    // stuff 
    SafeDelete(p); 
} 

Si imposta p a NULL, ma le copie del p al di fuori della funzione sono inalterati.

Tuttavia, se è necessario eseguire questa operazione, utilizzare il modello: i macro avranno sempre la possibilità di troncare altri nomi.

+0

Beh, un pazzo -1 ragazzo è in agguato arounnd. Uno. Questa risposta è corretta ... – gimpf

+3

+1 L'impostazione di un puntatore su NULL dopo averlo eliminato raramente ha senso. –

+0

Le impostazioni di un puntatore (membro di una classe) su NULL dopo la cancellazione potrebbero valere il tempo impiegato per aggiungere la linea alla funzione modello o la coppia di cicli della CPU sprecati. Se devi scavare nel codice fatto da qualcun altro con un design scadente che può facilmente individuare (schiantarsi) mostri ben nascosti: oggetti che usano oggetti già cancellati per qualche decimo di secondo. Se non stai usando una Z80, vale la pena. Salverà la testa per far saltare in aria. –

2

penso

#define SAFE_DELETE(pPtr) { delete pPtr; pPtr = NULL } è meglio

  1. il suo ok per chiamare Elimina se PPTR è NULL.Quindi se non è richiesto il controllo.
  2. nel caso in cui si chiami SAFE_DELETE (ptr + i), si verificherà un errore di compilazione.
  3. La definizione del modello creerà più istanze della funzione per ogni tipo di dati. Secondo me in questo caso, queste molteplici definizioni donano alcun valore.
  4. Inoltre, con la definizione della funzione modello, si ha un sovraccarico della chiamata di funzione.
+1

# 4 è davvero un non-problema. Una funzione così banale sarà sicuramente delineata da un compilatore semi-decente. – Dan

+1

# 3 è un non-problema per lo stesso motivo. In effetti, penso che sia anche # 2 dato che ptr + 1 non è un valore di l. Quindi sostanzialmente tutti i punti rilevanti non erano validi. –

+1

In aggiunta, SE si vorrebbe fare questo con una macro, si dovrebbe almeno usare questo: #define save_delete (p) do {delete p; p = NULL; } while (0) Pensaci, se non hai ancora visto un simile costrutto;) –

0

Come accennato un po 'sopra, la seconda è il migliore, non è una macro con potenziali effetti collaterali indesiderati, non hanno il controllo non necessari contro NULL (anche se ho il sospetto che si sta facendo che come un tipo controllare), ecc. Ma nessuno promette alcuna sicurezza. Se usi qualcosa come tr1 :: smart_ptr, assicurati di leggere i documenti su di loro e assicurati che abbia la semantica giusta per il tuo compito. Recentemente ho dovuto cercare e cancellare un'enorme perdita di memoria a causa di un collega che creava smart_ptrs in una struttura dati con collegamenti circolari :) (avrebbe dovuto usare weak_ptrs per riferimenti posteriori)

0

L'utilizzo di SAFE_DELETE appare davvero essere un programmatore C approccio per requalire la gestione della memoria integrata in C++. La mia domanda è: il C++ consentirà questo metodo di usare un SAFE_DELETE su puntatori che sono stati incapsulati correttamente come privati? Questa macro funzionerebbe SOLO sul puntatore dichiarato pubblico? OAD BAD !!

0

preferisco questa versione:

~scoped_ptr() { 
    delete this->ptr_; //this-> for emphasis, ptr_ is owned by this 
} 

Impostazione del puntatore nullo dopo l'eliminazione di esso è del tutto inutile, come l'unica ragione per cui si usa puntatori è quello di consentire un oggetto a cui fare riferimento in più posti contemporaneamente . Anche se il puntatore in una parte del programma è 0, potrebbero essercene altri che non sono impostati su 0.

Inoltre il modello macro/funzione safe_delete è molto difficile da usare, perché ci sono solo due posizioni che può essere usato se c'è un codice che può passare tra il nuovo ed eliminare per il puntatore dato.

1) All'interno di un blocco di cattura (...) che rilancia l'eccezione e anche duplicato accanto al blocco di cattura (...) per il percorso che non gira. (Anche duplicato accanto a ogni interruzione, ritorno, continua ecc. Che può consentire al puntatore di non rientrare)

2) Dentro un distruttore per un oggetto proprietario del puntatore (a meno che non vi sia alcun codice tra il nuovo e l'eliminazione che può buttare).

Anche se non esiste un codice che potrebbe generare quando si scrive il codice, questo potrebbe cambiare in futuro (tutto ciò che serve è che qualcuno arrivi e ne aggiunga un altro dopo il primo). È meglio scrivere codice in un modo che rimanga corretto anche in presenza di eccezioni.

L'opzione 1 crea così tanta duplicazione del codice ed è così facile da sbagliare che dubito persino di chiamarlo un'opzione.

L'opzione 2 rende ridondante safe_delete, poiché il ptr_ che si imposta su 0 uscirà dall'ambito della riga successiva.

In sintesi, non utilizzare safe_delete in quanto non è affatto sicuro (è molto difficile da utilizzare correttamente e genera codice ridondante anche se il suo utilizzo è corretto). Usa SBRM e puntatori intelligenti.

+0

Se si utilizza safeDelete per gestire i puntatori membri di una classe, i membri ben incapsulati, l'ipotesi di avere più puntatori agli stessi dati non riesce. I dati a cui vuoi accedere possono essere recuperati solo dall'oggetto e, grazie alla cancellazione sicura, qualsiasi utilizzo dell'oggetto contenitore dopo la sua cancellazione potrebbe essere facilmente individuato. L'utilizzo di safe_delete sul puntatore locale o solo su uno dei puntatori ai dati cancellati non ha significato. –