Domanda

Ho ereditato una grande base di codice C ++ e ho un compito per evitare eventuali eccezioni puntatore nullo che possono accadere nella base di codice. Sono ci sono strumenti di analisi statica a disposizione, penso niente, che si è utilizzato con successo.

Quali altre cose cerchi fuori per?

È stato utile?

Soluzione

Si può iniziare eliminando le fonti di NULL:

Cambia

if (error) {
    return NULL;
}

In

if (error) {
    return DefaultObject; // Ex: an empty vector
}

Se il ritorno oggetti predefiniti non si applica e la vostra base di codice già utilizza eccezioni, fare

if (error) {
    throw BadThingHappenedException;
}

Quindi, aggiungere la movimentazione in luoghi appropriati.

Se si sta lavorando con il codice legacy, si potrebbe fare alcune funzioni wrapper / classi:

ResultType *new_function() {
    ResultType *result = legacy_function();
    if (result) {
        return result;
    } else {
        throw BadThingHappenedException;
    }
}

Le nuove funzionalità dovrebbero iniziare a utilizzare le nuove funzioni e avere una corretta gestione delle eccezioni.

So che alcuni programmatori solo non si ottiene eccezioni, comprese le persone intelligenti come Joel . Ma, ciò che finisce accadendo restituendo NULL è che questo NULL si passa in giro come un matto in quanto tutti avrebbero basti pensare che non è il loro business per gestire e ritornare in silenzio. Alcune funzioni possono restituire il codice di errore, che va bene, ma il chiamante spesso finisce per tornare ancora-another-NULL in risposta agli errori. Poi, si vede un sacco di NULL controllo in ogni singola funzione, non importa quanto banale la funzione è. E, tutto quello che serve è solo un luogo che non verificare la presenza di NULL in crash il programma. Eccezioni costringono a pensare con attenzione sull'errore e decidere esattamente dove e come dovrebbe essere gestita.

Sembra che tu sei solo alla ricerca di soluzioni facili come strumenti di analisi statica (che si dovrebbe sempre usare). Cambiare puntatori ai riferimenti è anche una grande soluzione troppo. Tuttavia, C ++ ha la bellezza di Raii, che elimina la necessità di avere "try {} finally {}" in tutto il mondo, quindi penso che ne valga la vostra seria considerazione.

Altri suggerimenti

Se si gestiscono soprattutto la base di codice, uno dei più bassi livelli-di-sforzo e le cose di ritorno più alti si può fare è iniziare il refactoring i puntatori nude per riferimento contati puntatori .

Mi piacerebbe anche guardare qualcosa come Purificare quale strumento il codice per rilevare il danneggiamento della memoria sarà.

In primo luogo, come un elemento tecnico, C ++ non ha eccezioni puntatore nullo. Dereferenziazione un puntatore NULL è undefined comportamento, e sulla maggior parte dei sistemi fa sì che il programma di interrompere bruscamente ( "crash").

Per quanto riguarda gli strumenti, vi consiglio anche la domanda:

Sono statica del codice C ++ analyis strumenti vale la pena?

Per quanto riguarda le dereferenziazioni di puntatori NULL, in particolare, si consideri che un puntatore NULL dereference ha tre elementi principali:

  1. L'introduzione di un puntatore NULL.
  2. Il flusso di tale puntatore altrove nel programma.
  3. Il dereferenziamento di tale puntatore.

Il difficile per uno strumento di analisi statica è naturalmente passo 2, e gli strumenti si differenziano per quanto complicato un percorso possono precisione (cioè senza troppi falsi positivi) traccia. Potrebbe essere utile per vedere alcuni esempi specifici di insetti che si desidera catturare, al fine di una migliore consulenza sul tipo di strumento sarà più efficace.

Disclaimer: io lavoro per Coverity

.

Se non si desidera modificare il codice, è necessario utilizzare alcuni strumenti (vedi altre risposte). Ma per una parte speciale del problema (in cui si inserisce un puntatore in una funzione di usarlo) c'è un bel po 'Makro-Definition si potrebbe usare per trovare alcuni piccoli bastardi: (Senza tempo di overhead a rilascio-mode e aggiunge una condizione visibile al codice)

    #ifdef  NDEBUG

    #define NotNull(X) X

    #else // in Debug-Mode

    template<typename T> class NotNull;

    template<typename T> // template specialization only for pointer-types
    class NotNull<T*> {
    public:
        NotNull(T* object)
        : _object(object) {
            assert(object);
        }

        operator T*() const {
            return _object;
        }

        T* operator->() const {
            return _object;
        }
    private:
        T *_object;
    };

    #define NotNull(X) NotNull<X>

    #endif // in Debug-Mode

Basta cambiare tutte le funzioni da questo:

void increase(int* counter)  
{ .. } 

a questo

void increase(NotNull(int*) counter)
{ .. }  

P.S: prima trovato qui e può essere ottimizzato ulteriormente

Una domanda lato, è lo scopo di evitare questi perché non vogliono che il cliente di vedere un incidente? In molti casi puntatori nulli sono condizioni impreviste che devono essere gestiti subito, ma troppo spesso viene passato attraverso il sistema come una patata bollente.

Una volta ho lavorato su una base di codice in cui l'abitudine era con l'entrata in funzione, al primo controllo per tutti i puntatori nulli ed in caso affermativo, di ritorno. Il problema con questo è mentre lo strumento non è precipitato, in ultima analisi, ha generato dati non validi in silenzio. E cercando di eseguire il debug di questi problemi è stato difficile, perché ci potrebbero essere stati illegalmente i puntatori nulli essere passati in giro per molto tempo attraverso numerose funzioni prima che i risultati divennero intollerabili o alla fine ha dovuto manifestarsi.

Idealmente, si desidera affermazioni corrette, almeno durante il periodo di sviluppo, in modo da considerare una macro per nascondere o ridefinire l'asserzione per la produzione costruisce

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top