Gibt ein "NULL" -Objekt zurück, wenn das Suchergebnis nicht gefunden wurde

94

Ich bin ziemlich neu in C ++, daher neige ich dazu, während des Lernens mit vielen Java-Ismen zu entwerfen. Wenn ich in Java eine Klasse mit einer Suchmethode hätte, die ein Objekt Tvon einem Collection< T >zurückgibt, das einem bestimmten Parameter entspricht, würde ich dieses Objekt zurückgeben, und wenn das Objekt nicht in der Auflistung gefunden würde, würde ich zurückkehren null. Dann würde ich in meiner aufrufenden Funktion einfach nachsehenif(tResult != null) { ... }

In C ++ stelle ich fest, dass ich keinen nullWert zurückgeben kann, wenn das Objekt nicht vorhanden ist. Ich möchte nur einen 'Indikator' vom Typ T zurückgeben, der die aufrufende Funktion benachrichtigt, dass kein Objekt gefunden wurde. Ich möchte keine Ausnahme machen, weil es nicht wirklich ein außergewöhnlicher Umstand ist.

So sieht mein Code jetzt aus:

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;
}

Wie kann ich es ändern, damit ich diese Art von Marker geben kann?

aduric
quelle
6
Ausnahme und NULL sind nicht immer die einzigen Lösungen. Sie können häufig einen Wert für die Rückgabe auswählen, der angibt, dass er nicht gefunden wurde. std::find(first, last, value)Gibt beispielsweise zurück, lastwenn kein Element übereinstimmt.
Cascabel

Antworten:

70

In C ++ dürfen Referenzen nicht null sein. Wenn Sie optional null zurückgeben möchten, wenn nichts gefunden wird, müssen Sie einen Zeiger und keine Referenz zurückgeben:

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

Andernfalls sollten Sie eine Ausnahme auslösen, wenn Sie darauf bestehen, als Referenz zurückzukehren, wenn das Attribut nicht gefunden wird.

(Übrigens mache ich mir ein wenig Sorgen darüber, dass Ihre Methode constein Nicht- constAttribut ist und zurückgibt . Aus philosophischen Gründen würde ich die Rückgabe vorschlagen const Attr *. Wenn Sie dieses Attribut auch ändern möchten, können Sie es mit einer Nicht- constMethode überladen Rückgabe eines Nicht- constAttributs.)

Jesse Beder
quelle
2
Vielen Dank. Ist dies übrigens eine akzeptierte Art, eine solche Routine zu entwerfen?
Aduric
6
@aduric: Ja. Referenzen implizieren, dass das Ergebnis vorhanden sein muss. Zeiger implizieren, dass das Ergebnis möglicherweise nicht vorhanden ist.
Bill
7
Nur neugierig, sollen wir jetzt nullptrstatt NULLfür c ++ 11 zurückkehren?
Spectral
1
Ja, verwenden Sie in C ++ 11 und höher immer nullptr über NULL. Wenn Sie abwärtskompatibel mit Earliver-Versionen sein müssen, dann nicht
Conrad Jones
56

Hier gibt es mehrere mögliche Antworten. Sie möchten etwas zurückgeben, das möglicherweise vorhanden ist. Hier sind einige Optionen, die von meinen am wenigsten bevorzugten bis zu den am meisten bevorzugten reichen:

  • Rückgabe als Referenz und Signal kann ausnahmsweise nicht gefunden werden.

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

Es ist wahrscheinlich, dass das Nichtfinden von Attributen ein normaler Teil der Ausführung ist und daher nicht sehr außergewöhnlich. Die Handhabung hierfür wäre laut. Ein Nullwert kann nicht zurückgegeben werden, da es undefiniertes Verhalten ist, Nullreferenzen zu haben.

  • Rückkehr mit dem Zeiger

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

Es ist leicht zu vergessen, zu überprüfen, ob ein Ergebnis von getAttribute ein Nicht-NULL-Zeiger ist und eine einfache Fehlerquelle darstellt.

  • Verwenden Sie 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&>();
    }

Ein boost :: optional zeigt genau an, was hier vor sich geht, und bietet einfache Methoden, um zu überprüfen, ob ein solches Attribut gefunden wurde.


Randnotiz: std :: optional wurde kürzlich in C ++ 17 gewählt, daher wird dies in naher Zukunft ein "Standard" sein.

Kaz Dragon
quelle
+1 Ich würde nur zuerst boost :: optional erwähnen und nur kurz die anderen Alternativen erwähnen.
Nemanja Trifunovic
Ja, ich habe gesehen, dass boost :: optional irgendwo erwähnt wurde, aber ich dachte, dass es zu viel Overhead erfordert. Wenn die Verwendung der beste Ansatz für diese Art von Problemen ist, werde ich damit beginnen.
Aduric
boost::optionalerfordert nicht viel Overhead (keine dynamische Zuordnung), weshalb es so großartig ist. Die Verwendung mit polymorphen Werten erfordert das Umschließen von Referenzen oder Zeigern.
Matthieu M.
2
@MatthieuM. Es ist wahrscheinlich, dass der Overhead-Aduric nicht die Leistung war, sondern die Kosten für die Aufnahme einer externen Bibliothek in das Projekt.
Swoogan
Ein Nachtrag zu meiner Antwort: Beachten Sie, dass eine Bewegung im Gange ist, um optional als Standardkomponente zu standardisieren, wahrscheinlich für C ++ 17. Es lohnt sich also, über diese Technik Bescheid zu wissen.
Kaz Dragon
22

Sie können problemlos ein statisches Objekt erstellen, das eine NULL-Rückgabe darstellt.

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; 
};

Und irgendwo in einer Quelldatei:

static Attr AttrNull;
Mark Ransom
quelle
Sollte NodeNull nicht vom Typ Attr sein?
Aduric
2

Wie Sie herausgefunden haben, können Sie dies nicht so tun, wie Sie es in Java (oder C #) getan haben. Hier ist ein weiterer Vorschlag, Sie könnten die Referenz des Objekts als Argument übergeben und einen Bool-Wert zurückgeben. Wenn das Ergebnis in Ihrer Sammlung gefunden wird, können Sie es der übergebenen Referenz zuweisen und 'true' zurückgeben, andernfalls 'false'. Bitte beachten Sie diesen Code.

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;
}

Die obige Funktion muss den Operator anhand des Schlüssels 'Token' finden. Wenn er den gefundenen findet, gibt er true zurück und weist den Wert dem Parameter Operator & op zu.

Der Aufrufercode für diese Routine sieht folgendermaßen aus

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

Der Grund, warum Sie hier nicht NULL zurückgeben können, ist, dass Sie Ihren Rückgabetyp als deklariert haben Attr&. Das Trailing &macht den Rückgabewert zu einer "Referenz", die im Grunde genommen ein garantierter Nullzeiger auf ein vorhandenes Objekt ist. Wenn Sie null zurückgeben möchten, wechseln Sie Attr&zu Attr*.

JSB ձոգչ
quelle
0

Sie können nicht zurückkehren, NULLda der Rückgabetyp der Funktion ein Objekt referenceund kein a ist pointer.

Codaddict
quelle
-3

Sie können dies versuchen:

return &Type();
Erstellt
quelle
6
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich dabei, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage in Zukunft für Leser beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
NathanOliver
Dies gibt wahrscheinlich einen toten Verweis auf ein Objekt auf dem Methodenstapel zurück, nicht wahr?
Promonet