Sagen wir, wir haben eine Funktion der Form:
%Vor%Offensichtlich hat der obige Code ein Problem, wenn die Bedingung fehlschlägt, haben wir ein Problem, wie wir von dieser Funktion zurückkehren können. Der Kern meiner Frage ist, was ist der beste Weg, um mit einer solchen Situation umzugehen?
Dies ist kein syntaktisches Problem, sondern ein Designproblem. Sie müssen angeben, was ReturnOurObject()
zurückgeben soll, wenn SomeCondition
wahr ist. Das hängt hauptsächlich davon ab, wofür die Funktion verwendet wird. Und das hast du uns nicht gesagt.
Je nach Designproblemen sehe ich ein paar mögliche syntaktische Möglichkeiten:
SomeCondition
etwas Außergewöhnliches ist, mit dem Kunden nicht umgehen können, wäre das angemessen SomeCondition
immer halten sollte, sollte Ich würde in diesem Fall einen Zeiger anstelle einer Referenz verwenden. Tatsächlich ist dieses Kriterium (optionaler oder obligatorischer Rückgabewert), wie ich mich in erster Linie zwischen Zeigern und Referenzen entscheide.
Ich würde das wahrscheinlich tun:
%Vor%Und vielleicht auch:
%Vor%Anrufer haben dann einige Optionen:
%Vor%Sie gestalten die Schnittstelle einer Funktion anders, je nachdem, was die Vorbedingungen sind, d. h. wessen Verantwortung es ist, sicherzustellen, dass sie ihre Aufgabe erfüllen kann.
Wenn der Anrufer dafür verantwortlich ist, müssen sie dafür sorgen. Vielleicht möchten Sie es trotzdem überprüfen, nur um auf der sicheren Seite zu sein. Eine Exception zu werfen, wenn die sogenannten "Vorbedingungen" nicht erfüllt werden, macht sie eher zu einem Rat als zu Bedingungen, und es kann ziemlich gut funktionieren, vorausgesetzt, dass Sie den Laufzeit-Overhead von Überprüfungen nicht stören. Ich bin normalerweise ziemlich unversöhnlich mit Vorbedingungen, also könnte ich tatsächlich die bedingte Ausnahme durch eine Bestätigung ersetzen und dokumentieren, dass die Funktion nicht im falschen Modus aufgerufen werden darf. Es hängt davon ab, wie viel Kontrolle der Anrufer über den Modus hat - offensichtlich, wenn er sich willkürlich ändert (zB wenn "SomeCondition" ist "es ist kein Byte auf dem UART verfügbar"), dann brauchen Sie eine ganz andere Schnittstelle als wenn sie sich nur ändert wenn der Client-Code eine Funktion zum Ändern aufruft.
Wenn der Anrufer nicht dafür verantwortlich ist, dass der Modus richtig ist, sollte Ihre Schnittstelle keine Schecks schreiben, die Ihre Implementierung nicht auszahlen kann. Wenn es in diesem Fall "erwartet" wird, dass kein Objekt zurückgegeben wird, sollte die Rückgabe nicht durch Referenz erfolgen. Es sei denn, entweder (a) können Sie ein spezielles "Tut mir leid, es hat nicht funktionieren" -Objekt rascheln, auf das der Anrufer nachsehen kann, oder (b) es ist bequem, Ausnahmen in "erwarteten" Situationen zu werfen. IMO (b) ist in C ++ selten. (a) ist normalerweise nicht besser als einen Nullzeiger zurückgeben, aber gelegentlich ist es. Wenn Sie zum Beispiel eine leere Sammlung zurückgeben, um zu bedeuten, "es gibt nichts, was Sie hier sehen dürfen", könnte dies zu einem sehr sauberen Anrufcode führen, wenn DoSomethingElse()
"nichts tun" wäre. Neben den gewünschten Verantwortlichkeiten hängt die Schnittstelle von den erwarteten Anwendungsfällen ab.
Wenn Sie den Rückgabetyp nicht zu einem Zeiger ändern wollten, könnten Sie möglicherweise das Null-Objektmuster verwenden
Was ist die Lebensdauer von 'ourObject' und wo wird es erstellt?
Wenn Sie vorzeitig zurückkehren müssen, ohne unserObject zu berühren / aktualisieren, und es an dem Punkt existiert, an dem Sie zurückkehren, sehe ich kein Problem mit der Rückgabe eines Verweises darauf.
Wenn Sie mitteilen möchten, dass ein Fehler aufgetreten ist, müssen Sie wahrscheinlich eine Ausnahme auslösen, anstatt vorzeitig zurückzukommen, da der Vertrag, dem Ihre Funktion zugestimmt hat, einen Verweis auf "SomeObject" zurückgibt / p>
Wenn Sie einen Verweis auf ein temporäres Objekt zurückgeben, beheben Sie besser das Problem ...
Ich würde einen booleschen Wert zurückgeben und die Objektreferenz als Parameter an die Funktion übergeben:
%Vor%In diesem Fall wurde Ihr Objekt nur gefüllt, wenn die Funktion wahr zurückgibt.
Ich werde in Betracht ziehen, nur als Referenz zurückzukommen, wenn ich sicher bin, dass die Funktion niemals fehlschlägt (wahrscheinlich so, als würde man eine const-Referenz auf eine interne Membervariable zurückgeben). Wenn es innerhalb der Funktion mehrere Validierungen gibt, werde ich eine der folgenden Optionen berücksichtigen:
Wenn Sie keine Ausnahme auslösen möchten, können Sie Folgendes versuchen:
%Vor%Dies bedeutet, dass Sie keine statische Variable irgendwo herumliegen haben müssen, um zurückzukehren, und Sie können es immer noch ohne Parameter nennen. Ich nehme an, Sie würden davon ausgehen, dass der SodeObject () - Zustand standardmäßig der leere Zustand ist, so dass Sie SomeObject testen können, um zu sehen, ob es im leeren Zustand ist. Manchmal ist es auch nützlich, dies zu verwenden, um den Standard-Rückgabewert zu überschreiben, z. GetSomeObject (SomeObject ("Ich bin leer"))
1. Eine Ausnahme auslösen
2. Gibt ein Dummy-Fehlerobjekt
Ich glaube, die Antwort hängt davon ab, ob Sie die Art von Programmierer sind, der Ausnahmen für schlecht hält und sie nie benutzen will oder nicht. Wenn Sie sie verwenden, und dies ist wirklich eine Ausnahmebedingung, dann muss eine Ausnahme ausgelöst werden. Ja, die Benutzer müssen sich damit auseinandersetzen, aber einen Fehler zu verstecken, um vorzutäuschen, dass nichts passiert ist, ist einfach keine Option. Wenn Sie jedoch keine Ausnahmen verwenden, geben Sie einfach einen Zeiger oder einen booleschen Wert zurück und fügen Sie Ihrer Funktion einen nichtkonstanten Verweis auf SomeObject hinzu. Beide Lösungen sind einfach, aber wer sagt, dass das Überkomprimieren von Code den Code verbessert?
Wie die Funktion eingerichtet wird, müssen Sie eine SomeObject
zurückgeben oder eine Ausnahme auslösen. Wenn Sie etwas anderes machen wollen, müssen Sie die Funktion ändern. Es gibt keine anderen Möglichkeiten.
Wenn SomeCondition
niemals falsch ist, dann können Sie den Test im Code entfernen und stattdessen ein assert
eingeben, oder Sie können den Test behalten und eine Ausnahme auslösen. (Ich kann es nicht empfehlen, die Möglichkeit zu vernachlässigen, denn "nie falsch" bedeutet fast immer "niemals falsch, es sei denn, es ist etwas Schlimmes passiert und wurde nicht entdeckt"). p>
Es gibt die Möglichkeit, eine Art Nullobjekt zurückzugeben. Zeiger sind dafür nützlich, da es einen offensichtlichen Nullwert gibt, der falsch testet, aber Sie könnten ein Flag als out-Parameter zurückgeben oder ein spezielles Objekt zurückgeben, das als Null definiert ist. Dann müssen Sie den Rückgabewert bei jedem Aufruf testen, damit dies sinnvoll ist, und das ist wahrscheinlich nicht weniger aufwändig als die assert
oder throw
.
Wenn ich das täte, würde das von SomeCondition
abhängen. Wenn es Nebenwirkungen hat oder schwer zu berechnen ist, testen und werfen Sie eine Ausnahme, wenn sie falsch ist. Wenn es schwer ist zu berechnen, verwenden Sie assert
, so dass Sie es in der Produktion nicht tun müssen und darüber hinaus vergessen.
Dies ist die gleiche Situation wie der integrierte C ++ Operator dynamic_cast<>
. Es kann entweder in einen Zeigertyp oder einen Referenztyp umgewandelt werden. Wenn es sich um einen Zeigertyp handelt, wird bei einem Fehler NULL
zurückgegeben. Wenn es sich um einen Referenztyp handelt, wird bei einem Fehler die Ausnahme std::bad_cast
ausgelöst. Nach dem gleichen Beispiel würden Sie eine Ausnahme auslösen.
Tags und Links c++ function pass-by-reference