Restituisce un oggetto "NULL" se il risultato della ricerca non viene trovato


94

Sono abbastanza nuovo in C ++, quindi tendo a progettare con molti Java-ismi mentre sto imparando. Ad ogni modo, in Java, se avessi una classe con un metodo di "ricerca" che restituisse un oggetto Tda un Collection< T >che corrisponde a un parametro specifico, restituirei quell'oggetto e se l'oggetto non fosse trovato nella raccolta, tornerei null. Quindi nella mia funzione di chiamata vorrei solo controllareif(tResult != null) { ... }

In C ++, sto scoprendo che non posso restituire un nullvalore se l'oggetto non esiste. Voglio solo restituire un "indicatore" di tipo T che notifica alla funzione chiamante che non è stato trovato alcun oggetto. Non voglio fare un'eccezione perché non è davvero una circostanza eccezionale.

Questo è l'aspetto del mio codice in questo momento:

class Node {
    Attr& getAttribute(const string& attribute_name) const {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return NULL; // what should this be?
    }

private:
    vector<Attr> attributes;
}

Come posso cambiarlo in modo da poter dare quel tipo di pennarello?


6
Eccezione e NULL non sono sempre le uniche soluzioni. Spesso puoi scegliere un valore da restituire che indica non trovato: ad esempio, std::find(first, last, value)restituisce lastse nessun elemento corrisponde.
Cascabel

Risposte:


70

In C ++, i riferimenti non possono essere nulli. Se si desidera facoltativamente restituire null se non viene trovato nulla, è necessario restituire un puntatore, non un riferimento:

Attr *getAttribute(const string& attribute_name) const {
   //search collection
   //if found at i
        return &attributes[i];
   //if not found
        return nullptr;
}

Altrimenti, se insisti a tornare per riferimento, dovresti lanciare un'eccezione se l'attributo non viene trovato.

(A proposito, sono un po 'preoccupato che il tuo metodo sia conste restituisca un non constattributo. Per ragioni filosofiche, suggerirei di tornare const Attr *. Se anche tu potresti voler modificare questo attributo, puoi sovraccaricare con un non- constmetodo restituendo anche un non constattributo.)


2
Grazie. A proposito, è questo un modo accettato di progettare una tale routine?
aduric

6
@aduric: Sì. I riferimenti implicano che il risultato deve esistere. I puntatori implicano che il risultato potrebbe non esistere.
Bill

7
Solo curioso, torneremo ora nullptrinvece che NULLper c ++ 11?
Spectral

1
sì, usa sempre nullptr su NULL in C ++ 11 e versioni successive. se hai bisogno di essere retrocompatibile con le versioni Earliver, non farlo
Conrad Jones

56

Ci sono diverse possibili risposte qui. Vuoi restituire qualcosa che potrebbe esistere. Ecco alcune opzioni, che vanno dal meno preferito al più preferito:

  • Ritorna per riferimento e segnale impossibile trovare per eccezione.

    Attr& getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            throw no_such_attribute_error;
    }

È probabile che non trovare attributi sia una parte normale dell'esecuzione e quindi non del tutto eccezionale. Il trattamento per questo sarebbe rumoroso. Non è possibile restituire un valore null perché è un comportamento indefinito avere riferimenti null.

  • Ritorno tramite puntatore

    Attr* getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return &attributes[i];
       //if not found
            return nullptr;
    }

È facile dimenticare di controllare se un risultato di getAttribute sarebbe un puntatore non NULL ed è una facile fonte di bug.

  • Usa Boost.Optional

    boost::optional<Attr&> getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return boost::optional<Attr&>();
    }

Un boost :: optional indica esattamente cosa sta succedendo qui e dispone di metodi semplici per controllare se un tale attributo è stato trovato.


Nota a margine: std :: optional è stato recentemente votato in C ++ 17, quindi questa sarà una cosa "standard" nel prossimo futuro.


+1 Vorrei prima menzionare boost :: optional e menzionare solo brevemente le altre alternative.
Nemanja Trifunovic

Sì, ho visto boost :: optional menzionato da qualche parte, ma stavo pensando che richiedesse un sovraccarico eccessivo. Se usarlo è l'approccio migliore a questo tipo di problemi, inizierò a usarlo.
adurico

boost::optionalnon comporta molto overhead (nessuna allocazione dinamica), motivo per cui è così eccezionale. Usarlo con valori polimorfici richiede riferimenti o puntatori a capo.
Matthieu M.

2
@MatthieuM. È probabile che l'overhead a cui si riferiva aduric non fosse la performance, ma il costo dell'inclusione di una libreria esterna nel progetto.
Swoogan

Un addendum alla mia risposta: nota che è in corso un movimento per standardizzare l'opzionale come componente standard, probabilmente per quello che potrebbe essere C ++ 17. Quindi vale la pena conoscere questa tecnica.
Kaz Dragon

22

È possibile creare facilmente un oggetto statico che rappresenta un ritorno NULL.

class Attr;
extern Attr AttrNull;

class Node { 
.... 

Attr& getAttribute(const string& attribute_name) const { 
   //search collection 
   //if found at i 
        return attributes[i]; 
   //if not found 
        return AttrNull; 
} 

bool IsNull(const Attr& test) const {
    return &test == &AttrNull;
}

 private: 
   vector<Attr> attributes; 
};

E da qualche parte in un file sorgente:

static Attr AttrNull;

NodeNull non dovrebbe essere di tipo Attr?
adurico


2

Come hai capito, non puoi farlo nel modo in cui hai fatto in Java (o C #). Ecco un altro suggerimento, potresti passare il riferimento dell'oggetto come argomento e restituire il valore bool. Se il risultato viene trovato nella tua raccolta, puoi assegnarlo al riferimento passato e restituire "true", altrimenti restituire "false". Si prega di considerare questo codice.

typedef std::map<string, Operator> OPERATORS_MAP;

bool OperatorList::tryGetOperator(string token, Operator& op)
{
    bool val = false;

    OPERATORS_MAP::iterator it = m_operators.find(token);
    if (it != m_operators.end())
    {
        op = it->second;
        val = true;
    }
    return val;
}

La funzione sopra deve trovare l'Operatore contro la chiave 'token', se trova quella restituisce vero e assegna il valore al parametro Operatore & op.

Il codice del chiamante per questa routine è simile a questo

Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
    //Do something here if true is returned.
}

1

Il motivo per cui non puoi restituire NULL qui è perché hai dichiarato il tipo di ritorno come Attr&. Il finale &rende il valore restituito un "riferimento", che è fondamentalmente un puntatore garantito-non-essere-nullo a un oggetto esistente. Se vuoi essere in grado di restituire null, cambia Attr&in Attr*.


0

Non puoi tornare NULLperché il tipo restituito della funzione è un oggetto referencee non un file pointer.


-3

Puoi provare questo:

return &Type();

6
Sebbene questo snippet di codice possa risolvere la domanda, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
NathanOliver

Questo probabilmente restituisce un riferimento morto a un oggetto nello stack del metodo, non è vero?
mpromonet
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.