struct als Rückgabewert oder als Referenz
-
Hallo
Gibt man eine gefüllte Struktur besser als Rückgabewert zurück. Oder doch besser als Referenz in den Aufrufparametern.
Oder anhand welcher Kriterien entscheidet man welche Variante besser ist?
-
Im Allgemeinen ist es besser (weniger fehleranfällig, besser debugbar, ...) wenn der aufrufende Kontext den Übergabespeicherbereich bereitstellt, in welchem der untergeordnete Kontext seine Änderungen vornimmt.
Du kapselst damit das Speicherhandling im oberen Kontext, der untere kann sich auf "seine" Fachlichkeiten konzentrieren und muss sich nicht um irgendwelche Übergaberestriktionen kümmern.
Rückgabewerte aus Funktionen dienen oft zweckmäßigerweise zur Fehleranzeige, und dafür reicht dann int u.ä. aus.
-
@Wutz:
Und was hat das mit der Frage zu tun? Und zur Fehleranzeige nimmt man üblicherweise Exceptions. Output-Parameter sind bäh. Error-Returncodes sind auch bäh.@y-vonne:
Verwende das, was logisch Sinn macht.Die meisten Compiler "optimieren" die Rückgabe von Klassen (struct, class), indem sie der Funktion einen weitere, versteckten Zeiger-Parameter übergeben, auf den Speicherbereich, wo die Funktion ihren Returnwert reinkonstruieren soll. Es muss also nicht kopiert werden, die Funktion kann den Returnwert direkt dort erstellen wo er dann vom Aufrufer weiterverwendetw wird.
-
Hallo hustbear
Danke für deine Antwort. Du sagst Verwende das, was logisch Sinn macht. Das ist gut. Kannst du mir erklären woran ich erkenne was nun logisch Sinn macht.
Also für mich gibts da keine Logik.
-
Erstmal vorweg: dieser Thread gehört IMO eigentlich ins C++ oder RudP Forum. In MFC hat der eher nix verloren.
----
Achherrjeh, was ist logisch. Hm.
Wenn etwas ein neues Objekt ist, dann gibt es das vorher nicht. Also ich für mich logisch den Returnwert zu verwenden, und keine Referenz.Jetzt ist bloss noch die Frage: was ist ein neues Objekt?
Dabei muss man IMO Werte-Typen und Objekt-Typen (aka. Class-Types, Object-Types etc.) unterscheiden.
Einen Werte-Typ kennzeichnet, dass das Objekt das den Wert "speichert" keine Bedeutung hat, nur der Wert selbst. z.B. ein Integer oder ein String sind klassische Werte-Typen.
Einen Objekt-Typ kennzeichnet, dass er "Identität hat", d.h. es spielt sehr wohl eine Rolle um welches Objekt es sich dabei handelt. Objekt-Typen kann man auch oft nicht kopieren, bzw. nicht sinnvoll/vollständig kopieren. Einen Socket der mit einem Server irgendwo verbunden ist, kannst du nicht einfach kopieren. Der Server würde sich überhaupt nicht mehr auskennen, wenn auf einmal zwei Sockets mit ihm reden (mal davon abgesehen dass das über klassische Netzwerkprotokolle gar nicht ginge).
Zurück zu deiner Frage, bzw. zurück zur Frage wann ein neues Objekt erstellt wird. Bei Werte-Typen würde ich sagen: immer, weil das Objekt selbst keine Rolle spielt. D.h. wenn du Werte-Typen zurückgeben willst, dann mach es über den Returnwert.
Beispiel (ich verzichte hier mal auf Optimierungen wie pass-by-reference-to-const etc.):
void replace(string& inout_haystack, string needle, string replacement); // unlogisch string replace(string haystack, string needle, string replacement); // logischNoch klarer ist der Fall für mich, wenn ein Parameter kein "in/out" Parameter ist, sondern ein reiner "out" Parameter. Reine "out" Parameter werden von der aufgerufenen Funktion niemals gelesen, aber immer (vollständig) überschrieben. Hier entsteht klar etwas ganz neues, also warum so tun als ob es da vorher schon was gäbe, indem man eine Referenz auf dieses (logisch) nicht-existente Dinge zu übergeben.
Beispiel:
void get_name(some_object const& obj, string& out_name); // unlogisch string get_name(some_object const& obj); // logisch----
So. Nun zu den Objekt-Typen. Da ich oben geschrieben habe dass Objekt-Typen oft nicht kopierbar sind, könnte man annehmen, dass man sie nicht als Returnwert verwenden kann. Vor C++0x war das auch nur über üble Tricks möglich, bzw. indem man Zeiger zurückgegeben hat (rohe oder Smart-Pointer spielt dabei jetzt keine Rolle). Mit C++0x gibt es nun aber "verschiebbare" (movable) Typen, und Objekt-Typen kann man oft (meistens?) "verschiebbar" machen.
Andrerseits könnte man meinen, dass man bei Objekt-Typen gar keine Output-Parameter verwenden kann, da ja ein neues Objekt entsteht. Stimmt aber so auch nicht, da es ja sowas wie 2-Phase-Construction gibt. Man kann also immer noch sowas machen:
CFile file; // Phase 1 Foo(file); // erledigt intern Phase 2Da bei einem Objekt-Typ das Objekt selbst wichtig ist, sollte auch klar sein, ob ein neues Objekt entsteht oder nicht. Das Öffnen eines Files z.B. ist für mich klar das Erzeugen eines neuen Objekts, also Returnwert. Andrerseits entsteht beim Anpassen der Fenstergrösse an seinen Inhalt kein neues Objekt, also Referenz-Parameter.
Beispiel:
void create_temp_file(File& out_tempFile); // unlogisch File create_temp_file(); // logisch (C++0x) some_smart_ptr<File> create_temp_file(); // logisch (C++03) // Window fit_window_size(Window window); // geht nicht bzw. wäre total plem (=unlogisch) void fit_window_size(Window& inout_window); // logisch----
So. Andere werden das sicher anders sehen als ich, aber mir erscheint es halbwegs logisch. Ich hoffe du kannst damit 'was anfangen.
-
ps: es gibt natürlich, wie immer, gute Gründe die "unlogische" Variante zu wählen.
z.B. hat man oft mehr als einen Wert den man zurückgeben will. Wenn man aus einem bestimmten Grund kein pair/tuple verwenden kann/will, kann es Sinn machen Output-Parameter statt dessen zu verwenden.
Oder man hat Typen die man zwar ziemlich eindeutig als Werte-Typen einstufen kann/muss, aber man kann bestimmte Funktionen nicht so implementieren, dass dabei nicht unnötig kopiert wird. (Bzw. es wäre zu aufwendig es so zu machen).
Beispielsweise sind Bilder (Bitmaps, Texturen, ...) IMO ganz klar Werte-Typen - es zählt ja nur der Inhalt, nicht das Objekt selbst. Trotzdem würde ich eine SetPixel() Funktion nicht so implementieren:
Image SetPixel(Image original, Point position, Color color);
-
Dieser Thread wurde von Moderator/in Martin Richter aus dem Forum MFC (Visual C++) in das Forum C++ (auch C++0x) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Sehr schön. Vielen Dank. Das ich es so ausführlich beschrieben bekomme hätte ich nicht geacht. Hat mir auf jeden Fall sehr viel gebracht.
Mir erscheint es nun logisch meine Strucht als Returnwert zurük zu geben.

Danke!!!