C++ Pendant zu Java Objekt Referenz
-
In Java übergibt man ja Referenzen auf Objekte als Argument oder bekommt Objekt-Referenzen zurück und vermeidet so unnötige Kopien von Objekten automatisch. Was wäre der beste moderne C++ Pendant dazu? Zeiger, Referenzen, Smart-Pointer, Move Semantik?
// Das ist ja in Java gar kein Problem, es wird keine Kopie erzeugt und // es gibt keine hängenden Zeiger oder so was. // Wie würde das in C++ am besten aussehen? public MeineKlasseA gibAlles() { MeineKlasseA a = new MeineKlasseA(); return a; }
-
Ich würde Konzepte aus Java nicht 1:1 auf C++ übertragen.
Meinst du so etwas?class MeineKlasseA { // .... };
Wenn diese Klasse benötigt wird:
void f() { MeineKlasseA meineKlasse; // ... }
Andere Funktionen nehmen bspw. MeineKlasseA als Argument:
void g(const MeineKlasseA& a) { } void h(MeineKlasseA& a) { // von a können hier auch nichtkonstante Funktionen aufgerufen werden }
-
Ok, ja, also wenn man nur die Referenz auf ein Objekt in C++ als Argument übergibt, dann wird keine Kopie erzeugt, dass ist dann genauso effizient wie in Java.
Wie sieht es nun mit der Rückgabe eines Objektes in C++ aus, wenn dieses in der Methode erst erzeugt wird, ich aber keine Kopie haben möchte?
-
Ich nutze Pointer, wenn es auch mal NICHTS sein kann und die empfangende Funktion nach Abarbeitung das Objekt nicht mehr nutzt und schon gar nicht aus dem Speicher entfernt.
Referenzen auf Objekte übergebe ich, wenn immer valide und wiederum liegt die Verantwortung des Löschens bei der übergebenden Funktion.
Wenn Objekte übergeben werden, und die Verantwortung des Löschens nicht klar ist, nutze ich std::shared_ptr<T>, dann löscht halt der letzte Nutzer automatisch.
-
@CppConst sagte in C++ Pendant zu Java Objekt Referenz:
Wie sieht es nun mit der Rückgabe eines Objektes in C++ aus, wenn dieses in der Methode erst erzeugt wird, ich aber keine Kopie haben möchte?
Kommt drauf an... ich würde in den meisten Fällen einfach ein Return machen und mich auf RVO verlassen (https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization).
Man kann auch smart pointer zurück geben, wenn aus irgendeinem Grund nötig.
-
Und wenn man sich nicht auf RVO verlassen möchte, was kann man dann empfehlen?
-
Deine Fragen sind viel zu allgemein. Bevor einem ein Profiler nicht gesagt hat daß es ein Flaschenhals ist macht man sich über sowas sowieso keine Gedanken.
-
@CppConst sagte in C++ Pendant zu Java Objekt Referenz:
Und wenn man sich nicht auf RVO verlassen möchte, was kann man dann empfehlen?
Sich auf RVO zu verlassen.
-
Ist halt nicht einfach, wenn man von Java auf C++ umsteigt und bei mir in Java werden haufenweise Objekte in Methoden erzeugt und dann einfach zurückgegeben. Und bei C++ habe ich eben gelesen, dass es da zu Kopien kommen kann. Das versuche ich im Vorfeld schon sicher zu eliminieren und dachte da gibt es was Allgemeines für.
-
@CppConst sagte in C++ Pendant zu Java Objekt Referenz:
Das versuche ich im Vorfeld schon sicher zu eliminieren
Ja. Lass das. Das ist vergebene Liebesmüh'. Schreib' schönen(tm) Code.
-
Ok, dann kümmere ich mich darum wenn es Probleme gibt und erzeuge erstmal einfach einen std::string und geben den so wie er ist mit return zurück.
-
Gute Idee.
-
Wenn es um String geht... https://www.c-plusplus.net/forum/topic/347040/einen-string-an-eine-funktion-übergeben
-
@Helmut-Jakoby Es geht um Rückgabe. Aber, gratuliere, du hast deinen Werbelink wieder erfolgreich angebracht.
-
Damit RVO greifen kann, muss meine Objekt aber einen Move Konstruktor haben, oder?
-
@CppConst sagte in C++ Pendant zu Java Objekt Referenz:
Damit RVO greifen kann, muss meine Objekt aber einen Move Konstruktor haben, oder?
Nein.
Move ist auch Arbeit, wenn auch oft weniger als eine Kopie. Aber wenn es geht, ist "gar nichts tun" besser als move. Move passiert, wenn "nichts tun" nicht geht.
Siehe auch https://youtu.be/hA1WNtNyNbo
-
Bzw. s. Copy elision
-
@CppConst sagte in C++ Pendant zu Java Objekt Referenz:
Damit RVO greifen kann, muss meine Objekt aber einen Move Konstruktor haben, oder?
Bei RVO + copy-elision wird das Objekt direkt dort erstellt wo es dann nach dem Funktionsaufruf "weiterleben" soll.
Foo makeFoo() { Foo f{"blah"}; // 1 f.something(1); f.somethingElse(1, 2, 3); return f; // Hier KEIN std::move() - mit move würde man NRVO _verhindern_ } void fun() { Foo foo{makeFoo()}; // 2 }
Das Objekt das in (1) erstellt wird, wird also direkt im Speicher der Variable von Zeile (2) erstellt. Hier wird weder ein Move-Konstruktor noch ein Copy-Konstruktor verwendet.
-
Danke für die genaue Erklärung, so langsam begreife ich es. Ist nicht so einfach C++ richtig zu verstehen. Ich habe zwar schon einige Sachen mit C++ gemacht, habe mir allerdings über solche Dinge keine Gedanken gemacht, bis ich so ein CppCon Video gesehen hatte, wo es darum ging wie man Overhead vermeiden kann, dann kamen die Fragen und nun möchte ich C++ besser kennen lernen.
-
Ergänzung:
Foo makeFoo() { Foo f{"blah"}; // 1 f.something(1); f.somethingElse(1, 2, 3); return f; // Hier KEIN std::move() - mit move würde man NRVO _verhindern_ }
Tatsächlich garantierte der Standard vor guaranteed copy elision (also vor C++17) ab C++11, dass der identifier
f
ein rvalue ist. D.h.move(f)
war implizit. Es haette also schon rein technisch niemals geholfen, das zu schreiben (abgesehen davon, dass NRVO lange vor guaranteed copy elision implementiert war).