Noch einmal const & usw.
-
Ich muss leider noch einmal mit dem Thema "übergeben von Parameter" und const & nerven, weil ich das immer noch nicht so richtig geschnallt habe und versuche mir einfache Regeln für den Einsatz zu erstellen. Zeiger lass ich jetzt noch mal weg...
class Foo // Version 1 { public: Foo(std::string name):name(name){}; std::string getName(); private: std::string name; };
Version 1 funktioniert, ist aber nicht optimal, weil der String bei der Übergabe kopiert wird (Performance) und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
class Foo // Version 2 { public: Foo(const std::string name):name(name){}; // const std::string getName(); private: std::string name; };
Version 2 funktioniert auch, der String wird aber immer noch bei der Übergabe kopiert. Das zweite Problem sollte damit aber gelöst sein.
class Foo // Version 3 { public: Foo(std::string& name):name(name){}; // & std::string getName(); private: std::string name; };
Version 3 vermeidet das Kopieren des String, aber jetzt kann ich nur noch lvalues übergeben. Damit funktioniert das:
Foo foo{"Hallo Welt"};
nicht mehr.
class Foo // Version 4 { public: Foo(const std::string& name):name(name){}; // const & std::string getName(); private: std::string name; };
Version 4 scheint zumindest alle o.g. Probleme zu lösen.
class Foo // Version 5 { public: Foo(const std::string& name):name(name){}; std::string getName() const; // const private: std::string name; };
Die Rückgabefunktion sollte noch const sein, weil sie die Instanz beim Aufruf nicht verändert und die Funktion nicht genutzt werden kann, wenn Foo als const verwendet wird:
string getSomething(const Foo& foo) { return foo.getDescription(); // geht sonst nicht! }
Stimmt das, was ich mir hier zusammengereimt habe, oder gibt es noch etwas zu ergänzen?
Es wird doch trotzdem noch eine Kopie des Parameters erstellt, wenn dieser an die Membervariable "name" zugewiesen wird (nicht dargestellt), oder? Ist aber auch sinnvoll in diesem Fall.
Meine Regel wäre jetzt: Falls die Klasse den Parameter nicht verändert übergebe ich ihn als const &. Das sollte i.d.R. so der Fall sein.
Danke für die Erleuchtung,
temi
-
Probiere aus, ob alle Behauptungen zutreffen.
-
manni66 schrieb:
Probiere aus, ob alle Behauptungen zutreffen.
Habe ich gemacht, während ich die einzelnen Varianten hier hinzugefügt habe.
Das was jeweils darunter steht sind meine Schlussfolgerungen.
-
temi schrieb:
Version 1 funktioniert, ist aber nicht optimal, weil der String bei der Übergabe kopiert wird (Performance) und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
probiere *das* aus. versuche, die übergebene variable zu ändern und schau, ob das jemand von außen merkt.
-
temi schrieb:
Habe ich gemacht, während ich die einzelnen Varianten hier hinzugefügt hab.
Nein
-
dove schrieb:
temi schrieb:
Version 1 funktioniert, ist aber nicht optimal, weil der String bei der Übergabe kopiert wird (Performance) und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
probiere *das* aus. versuche, die übergebene variable zu ändern und schau, ob das jemand von außen merkt.
Ich habe die Klasse erweitert:
void setName(const std::string& name) { //name = "Ade du blöde Welt"; fehler beim compilieren => const!!! this->name = name; }
Ich habe es in allen Varianten probiert. Hat sich wie erwartet verhalten.
Stimmt das, was ich mir hier zusammengereimt habe, oder gibt es noch etwas zu ergänzen?
Die erste Hälfte beantworte ich mir mal mit ja. Zweite Hälfte?
Es wird doch trotzdem noch eine Kopie des Parameters erstellt, wenn dieser an die Membervariable "name" zugewiesen wird (nicht dargestellt), oder? Ist aber auch sinnvoll in diesem Fall.
Ist zumindest für mich so logisch, es sein den die Membervariable wäre auch & (bzw. const &), aber das ist dann vermutlich nicht sinnvoll. Richtig?
Meine Regel wäre jetzt: Falls die Klasse den Parameter nicht verändert übergebe ich ihn als const &. Das sollte i.d.R. so der Fall sein.
???
-
manni66 schrieb:
temi schrieb:
Habe ich gemacht, während ich die einzelnen Varianten hier hinzugefügt hab.
Nein
Ist meine Webcam an, oder hast du hinter mir gestanden?
Sorry, ich weiß, dass du gerne zum Nachdenken anregst, aber ich steh grad auf dem Schlauch.
-
dove schrieb:
temi schrieb:
Version 1 funktioniert, ist aber nicht optimal, weil der String bei der Übergabe kopiert wird (Performance) und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
probiere *das* aus. versuche, die übergebene variable zu ändern und schau, ob das jemand von außen merkt.
Mach das
-
Ich glaube ich weiß, was ihr meint. Die von außen übergebene Variable kann natürlich nicht innerhalb der Funktion von Variante 1 geändert werden, weil diese nur auf die Kopie der Variablen zugreifen kann.
Das hatte ich nicht explizit hingeschrieben und auch anders gemeint, mein Fehler.
und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
Ich hatte vorher einen Text zur "const correctness" durchgelesen und das so interpretiert, dass es guter Stil wäre den Aufrufer durch die Angabe von "const" direkt darauf hinzuweisen, dass es garantiert keine Änderung der übergebenen Variable geben wird.
-
Keine Antwort = alles richtig?
-
Ich möchte das Thema gerne noch etwas erweitern:
class Foo { public: Foo(const std::string& name):name(name){}; std::string getName() { return name; } private: const std::string name; }; class Bar { public: void setFoo(std::unique_ptr<Foo> pFoo) { foo = std::move(pFoo); // move wegen unique_ptr } std::string getName() { return foo->getName(); } private: std::unique_ptr<Foo> foo; }; int main(int argc, char *argv[]) { Bar bar; auto foo = std::unique_ptr<Foo>(new Foo{"Hallo Welt"}); cout << foo.get()->getName() << endl; bar.setFoo(foo); // hier ist das Problem cout << bar.getName() << endl; }
bar.setFoo(foo);
geht nicht, weil
void setFoo(std::unique_ptr<Foo> pFoo)
eine Übergabe per value ist und das funktioniert mit unique_ptr ja nicht, weil keine Kopie möglich ist.
Also erst mal:
void setFoo(std::unique_ptr<Foo>& pFoo)
dann klappt das auch.
Jetzt möchte ich aber auch dieses machen:
int main(int argc, char *argv[]) { Bar bar; //auto foo = std::unique_ptr<Foo>(new Foo{"Hallo Welt"}); //cout << foo.get()->getName() << endl; bar.setFoo(std::unique_ptr<Foo>(new Foo{"Hallo Welt"})); // Fehler: invalid initialization of non-const reference cout << bar.getName() << endl; }
Geändert in:
void setFoo(const std::unique_ptr<Foo>& pFoo)
void setFoo(const std::unique_ptr<Foo>& pFoo) { foo = std::move(pFoo); // Fehler: Use of deleted function... }
Meine Erklärung wäre dazu, dass der Parameter ja const ist und damit nicht verändert werden darf. Soweit so gut. Aber wie erreiche ich jetzt mein Ziel?
-
temi schrieb:
Keine Antwort = alles richtig?
Hattest du 24/7 gebucht? Moment, ich schau mal nach...
Nein, ich sehe auch keinen Zahlungseingang.
-
temi schrieb:
und der Anwender der Klasse nicht davon ausgehen kann, dass die Klasse die übergebene Variable nicht verändert (auch wenn sie es nicht tut).
Ich hatte vorher einen Text zur "const correctness" durchgelesen und das so interpretiert, dass es guter Stil wäre den Aufrufer durch die Angabe von "const" direkt darauf hinzuweisen, dass es garantiert keine Änderung der übergebenen Variable geben wird.
Das wirst du wohl falsch verstanden haben.
1. ist das dem Aufrufer völlig schnuppe
2. kann man den const Parameter jederzeit kopieren und dann ändern
-
void setFoo(std::unique_ptr<Foo>& pFoo)
Wer so etwas schreibt hat ziemlich sicher keine Ahnung.
Entweder übernimmt setFoo den Besitz. Dann ist es keine Referenz und der Wert muss gegebenenfalls mit move reingeschoben werden.
Oder der Besitz wird nicht übernommen. Dann wird Foo* übergeben und kein Smartpointer.
-
manni66 schrieb:
[/code]
Wer so etwas schreibt hat ziemlich sicher keine Ahnung.Natürlich habe ich keine Ahnung, sonst würde ich ja nicht fragen, oder?
Du musst natürlich nicht nett und freundlich sein, aber ich finde es wäre kein Schaden.
-
temi schrieb:
Aber wie erreiche ich jetzt mein Ziel?
Was ist denn dein Ziel?
-
temi schrieb:
Ich möchte das Thema gerne noch etwas erweitern:
auto foo = std::unique_ptr<Foo>(new Foo{"Hallo Welt"}); cout << foo.get()->getName() << endl; bar.setFoo(foo); // hier ist das Problem }
bar.setFoo(foo);
geht nicht, weil
void setFoo(std::unique_ptr<Foo> pFoo)
eine Übergabe per value ist und das funktioniert mit unique_ptr ja nicht, weil keine Kopie möglich ist.
Wenn du foo in deiner main nicht mehr nutzen willst nach dem Call von bar.setFoo, dann musst du schreiben:
bar.setFoo(move(foo));
. D.h. Übergabetyp ist derunique_ptr<Foo>
(keine Referenz auf unique_ptr!). Und wenn du Ownership nicht transferieren willst, dann einfachFoo*
übergeben (bzwconst Foo*
wenn möglich)Ich hatte gehofft, dass diese Fälle ziemlich deutlich in den 3 von dove und mir geposteten gotw-Links erklärt wäre.
-
wob schrieb:
Ich hatte gehofft, dass diese Fälle ziemlich deutlich in den 3 von dove und mir geposteten gotw-Links erklärt wäre.
Großes SORRY und auch Dank dafür! Ich hatte das natürlich (neben Vielem anderen) durchgelesen, aber irgendwie auch schon wieder verdrängt. Ist mir dann gestern, aber aufgefallen und ich habe die Lesestunde bereits wiederholt. Ich bin halt nicht mehr der Jüngste, aber irgendwann kapier ich es auch noch.
Gruß,
temi