È sicuro eliminare un puntatore NULL?


299

È sicuro eliminare un puntatore NULL?

Ed è un buon stile di codifica?


21
La buona pratica è scrivere programmi C ++ senza una sola chiamata a delete. Usa invece RAII . Cioè, usa std::vector<T> v(100);invece di T* p = new T[100];, usa puntatori intelligenti come unique_ptr<T>e shared_ptr<T>che si occupano dell'eliminazione invece di puntatori grezzi ecc.
fredoverflow

8
grazie a make_shared(c ++ 11) e make_unique(c ++ 14) il tuo programma dovrebbe contenere zero di newedelete
sp2danny,

2
Potrebbero esserci ancora alcuni rari casi che richiedono nuovo / elimina, ad esempio atomico <T *>: atomico <unique_ptr <T>> non è consentito e atomico <shared_ptr <T>> ha un sovraccarico che può essere inaccettabile in alcuni casi.
atb

2
Per dichiarare una classe con gestione delle risorse usando RAII è necessario chiamare new ed elimina giusto? Oppure stai dicendo che esiste una classe template per nascondere anche questo.
VinGarcia,

2
@VinGarcia Il punto è che la maggior parte del codice utente / client (ovvero: non libreria) non dovrebbe mai scrivere newo delete. Le classi progettati per gestire le risorse, in cui i componenti standard non possono fare il lavoro, possono naturalmente fare quello che devono fare, ma il punto è che essi fanno la roba brutta con la memoria riescono, non il codice utente finale. Quindi, crea la tua libreria / classe helper per fare new/ deletee usa quella classe invece di loro.
underscore_d

Risposte:


265

deleteesegue comunque il controllo, quindi controllarlo dalla tua parte aggiunge sovraccarico e sembra più brutto. Una molto buona pratica è l'impostazione del puntatore a NULL dopo delete(aiuta a evitare la doppia eliminazione e altri problemi di corruzione della memoria simili).

Mi piacerebbe anche se deleteper impostazione predefinita il parametro fosse impostato su NULL come in

#define my_delete(x) {delete x; x = NULL;}

(Conosco i valori R e L, ma non sarebbe carino?)


72
Si noti che possono esserci ancora diversi altri puntatori che puntano allo stesso oggetto anche se si imposta uno su NULL al momento dell'eliminazione.
qc

13
Nella maggior parte dei casi nel mio codice, il puntatore esce dall'ambito una volta eliminato. Molto più sicuro della semplice impostazione su NULL.
jalf

142
Una pratica molto divina non sta impostando il puntatore su NULL dopo l'eliminazione. L'impostazione di un puntatore su NULL dopo averlo eliminato maschera gli errori di allocazione della memoria, il che è una cosa molto negativa. Un programma corretto non elimina due volte un puntatore e un programma che elimina due volte un puntatore dovrebbe arrestarsi in modo anomalo.
Damon,

15
@Alice: è irrilevante ciò che dice lo standard a tale riguardo. Lo standard ha definito l'eliminazione di un puntatore null essendo valido per qualche motivo assurdo 30 anni fa, quindi è legale (molto probabilmente un lascito C). Ma cancellare lo stesso puntatore due volte (anche dopo aver cambiato il suo schema di bit) è ancora un grave errore del programma. Non dalla formulazione dello standard, ma dalla logica e dalla proprietà del programma. Come l'eliminazione di un puntatore null, poiché il puntatore null corrisponde a nessun oggetto , quindi non è possibile eliminare nulla. Un programma deve sapere esattamente se un oggetto è valido e chi lo possiede e quando può essere eliminato.
Damon,

27
@Damon Tuttavia, nonostante queste abrogazioni delle tue regole di proprietà draconiane, le strutture senza serratura sono decisamente più robuste di quelle basate su serratura. E sì, i miei colleghi mi amano davvero per il profilo di esecuzione migliorato fornito da queste strutture e per la rigorosa sicurezza del thread che mantengono, che consente di ragionare più facilmente sul codice (ottimo per la manutenzione). Tuttavia, nulla di tutto ciò né il tuo attacco personale implicito hanno a che fare con una definizione di correttezza, validità o proprietà. Ciò che proponi è una buona regola empirica, ma non è una legge universale né è sancita dallo standard.
Alice

77

Dalla bozza C ++ 0x standard.

$ 5.3.5 / 2 - "[...] In entrambe le alternative, il valore dell'operando di delete può essere un valore puntatore null. [... '"

Ovviamente, nessuno avrebbe mai "eliminato" un puntatore con valore NULL, ma è sicuro farlo. Idealmente non si dovrebbe avere un codice che cancella un puntatore NULL. Ma a volte è utile quando la cancellazione di puntatori (ad esempio in un contenitore) avviene in un ciclo. Poiché l'eliminazione di un valore del puntatore NULL è sicura, si può davvero scrivere la logica di cancellazione senza controlli espliciti per l'operando NULL da eliminare.

A parte questo, lo standard C $ 7.20.3.2 dice anche che "libero" su un puntatore NULL non fa nulla.

La funzione libera fa sì che lo spazio indicato da ptr venga deallocato, ovvero reso disponibile per un'ulteriore allocazione. Se ptr è un puntatore nullo, non si verifica alcuna azione.


2
Vorrei davvero questa risposta per le sue citazioni se non introducesse intenzionalmente inefficienza nel codice non ottimizzato. Come afferma la risposta accettata, la cancellazione di un puntatore null è no-op. Pertanto, verificare se un puntatore è nullo prima di eliminarlo è completamente estraneo.
codetaku,

47

Sì, è sicuro.

Non c'è nulla di male nell'eliminare un puntatore nullo; spesso riduce il numero di test alla fine di una funzione se i puntatori non allocati vengono inizializzati a zero e quindi semplicemente eliminati.


Poiché la frase precedente ha causato confusione, un esempio - che non è un'eccezione sicura - di ciò che viene descritto:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Ci sono tutti i tipi di nits che possono essere scelti con il codice di esempio, ma il concetto è (spero) chiaro. Le variabili del puntatore vengono inizializzate su zero in modo che le deleteoperazioni alla fine della funzione non debbano verificare se sono non nulle nel codice sorgente; il codice della libreria esegue comunque quel controllo.


Ho dovuto leggerlo alcune volte per dargli un senso. Devi dire che li inizializzi a zero all'inizio del metodo, o durante quello, non alla coda, sicuramente? Altrimenti rimuoveresti sia l'azzeramento che l'eliminazione.
Marchese di Lorne,

1
@EJP: Un contorno non del tutto inverosimile di una funzione potrebbe essere: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Non ho mostrato alcuna struttura a blocchi o salti, ma le delete[]operazioni alla fine sono sicure a causa delle inizializzazioni all'inizio. Se qualcosa passasse alla fine dopo che x1era stato allocato e prima che y1fosse allocato e non vi fosse l'inizializzazione di y1, allora ci sarebbe un comportamento indefinito - e mentre il codice poteva verificare la nullità (di x1e y1) prima delle eliminazioni, non è necessario fare così.
Jonathan Leffler,

22

L'eliminazione di un puntatore null non ha alcun effetto. Non è un buon stile di codifica necessariamente perché non è necessario, ma non è neanche male.

Se stai cercando buone pratiche di codifica, considera di utilizzare invece i puntatori intelligenti, quindi non è necessario delete.


20
il tempo in cui le persone vogliono eliminare un puntatore NULL è quando non sono sicuri che contenga NULL ... se sapessero che era NULL allora non prenderebbero in considerazione la possibilità di eliminare e quindi chiedere ;-).
Tony Delroy,

@Tony: Il mio punto era solo che non avrebbe avuto alcun effetto, e la presenza di tale codice che cancella un puntatore che a volte contiene NULL non è necessariamente negativa.
Brian R. Bondy,

3
I controlli ridondanti dell'IMO sono certamente negativi per prestazioni, leggibilità e manutenibilità.
paulm,

@paulm OP non sta certamente parlando di quel tipo di cattivo, più di Seg Fault / UB di cattivo tipo.
Inverno


3

È sicuro a meno che non si sia sovraccaricato l'operatore di eliminazione. se hai sovraccaricato l'operatore di eliminazione e non gestisci la condizione nulla, non è affatto sicuro.


Potresti aggiungere qualche spiegazione per la tua risposta?
Marcin Nabiałek,

-2

Ho sperimentato che è non è sicura (VS2010) per cancellare [] NULL (vale a dire la sintassi array). Non sono sicuro che questo sia conforme allo standard C ++.

Si è sicuro di NULL eliminazione (sintassi scalare).


6
Questo è illegale e non ci credo.
Konrad Rudolph,

5
Dovresti essere in grado di eliminare qualsiasi puntatore nullo. Quindi, se si sta rompendo per te, allora probabilmente hai un bug nel codice che lo mostra.
Mistico il

3
§5.3.2 Nella seconda alternativa (delete array), il valore dell'operando di delete può essere un valore puntatore nullo o un valore puntatore risultante da una nuova espressione della matrice precedente.
sp2danny,

1
@Opux Il comportamento di VS2010 asserito nella risposta. Come spiegato in altri commenti, è sicuro delete[] NULL.
Konrad Rudolph,

1
@Opux Ecco perché ho scritto "Non ci credo" piuttosto che "è sbagliato". Ma ancora non lo faccio, e sarebbe una violazione abbastanza scandalosa e stupida dello standard. VC ++ è generalmente abbastanza bravo a seguire le restrizioni dello standard, e i luoghi in cui le viola hanno senso storicamente.
Konrad Rudolph,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.