new Operator in einer Methode
-
- Ja.
- Nein.
-
nightrider schrieb:
Hallo,
ich komme aus der Java Welt und habe zwei ganz einfache Fragen:
Ich habe folgende Funktion:
void foo() { A *a = new A(); }Liege ich mit folgender Aussage richtig?
Ein Objekt von A wird auf dem Heap angelegt. Die Variable a liegt auf dem Stack. nach Beendigung von foo() wird a vom Stack gepoppt, das Objekt A liegt jedoch noch am Heap, ist jedoch nicht mehr ereichbar.Ja.
Muss ich also bevor die Methode verlassen wird zwingend ein delete ausführen, damit der Speicher von A wieder freigegeben wird?
Ja und Nein.
Ja: Du musst wieder aufräumen.
Nein: Wozu überhaupt erst Dreck machen? In C++ muss, im Gegensatz zu Java, nicht alles auf den Heap. Genaugenommen ist das sogar eher die Ausnahme. Und falls doch, dann Kapsel das in Stackobjekten die ein Heapobjekt halten und dieses selbstständig löschen, wenn sie den Scope verlassen. Stichwort für Google: RAII. Das machen alle Standardklassen so. Und falls du doch mal Pointersemantik wegen exotischer Objektlebenszeiten brauchst, dann nimm Pointerklassen die das Heapobjekt löschen, wenn alle Referenzen den Scope verlassen haben. Stichwort: Smartpointer.sind folgende zwei Anweisungen semantisch equivalent?
A a = A(); A a;Ja, in beiden Fällen wird nur der Defaultkonstruktor benutzt.
-
EOutOfResources schrieb:
- Ja.
- Nein.
2: So? Probier mal aus:
#include <iostream> struct foo { foo(const foo& f) {std::cout << "Kopiert.\n";} foo() {std::cout << "Default.\n";} foo& operator=(const foo& f){std::cout << "Zugewiesen.\n";} }; int main() { foo a; foo b = foo(); }
-
SeppJ schrieb:
Ja, in beiden Fällen wird nur der Defaultkonstruktor benutzt.
Bist du dir da sicher? Ich dachte es wird der Defaultkonstruktor und dann der Kopierkonstruktor ausgeführt. Wäre ja auch intuitiv.
-
EOutOfResources schrieb:
SeppJ schrieb:
Ja, in beiden Fällen wird nur der Defaultkonstruktor benutzt.
Bist du dir da sicher? Ich dachte es wird der Defaultkonstruktor und dann der Kopierkonstruktor ausgeführt. Wäre ja auch intuitiv.
Siehe obiges edit. Ich könnte auch die Stelle im Standard suchen, aber gerade keine Zeit.
-
Ja, du hast recht. Aber das ist unintuitiv und inkonsistent.
-
EOutOfResources schrieb:
SeppJ schrieb:
Ja, in beiden Fällen wird nur der Defaultkonstruktor benutzt.
Bist du dir da sicher? Ich dachte es wird der Defaultkonstruktor und dann der Kopierkonstruktor ausgeführt. Wäre ja auch intuitiv.
Ist auch so. Allerdings erzeugt der Defaultkonstruktor ein temporäres Objekt, also ist copy elision a.k.a. RVO möglich.
-
ich denke auch, dass es default konstruiert wird und dann zugewiesen, allerdings wird jeder compiler schon beim ersten optimieren, nur den default konstruktor aufrufen
-
Ich hab's im MSVC 2008 ohne Optimierungen getestet. Immer noch zwei DefCToren...
Skym0sh0 schrieb:
ich denke auch, dass es default konstruiert wird und dann zugewiesen
Zugewiesen sicher nicht. Du meinst Kopierkonstruiert.
-
EOutOfResources schrieb:
Ich hab's im MSVC 2008 ohne Optimierungen getestet. Immer noch zwei DefCToren...
Skym0sh0 schrieb:
ich denke auch, dass es default konstruiert wird und dann zugewiesen
Zugewiesen sicher nicht. Du meinst Kopierkonstruiert.
Compileroptimierung != Sprachoptimierung. Wäre auch doof wenn der Compiler plötzlich was anderes machen dürfe, bloß weil die Optimierung eingschaltet wird.
-
SeppJ schrieb:
EOutOfResources schrieb:
- Ja.
- Nein.
2: So? Probier mal aus:
So? Probier mal aus:
#include <iostream> struct foo { foo() {std::cout << "Default.\n";} foo& operator=(const foo& f){std::cout << "Zugewiesen.\n";} private: foo(const foo& f) {std::cout << "Kopiert.\n";} }; int main() { foo a; foo b = foo(); //Ups!!! }
-
Argh. Gut erkannt, dass es da doch noch einen kleinen Unterschied gibt.

-
Da bin ich jetzt nicht hinterhergekommen, sehe keinen signifikanten Unterschied bloss dass 'hmpf's Stück in IDEOne nicht kompiliert

-
hä? schrieb:
Da bin ich jetzt nicht hinterhergekommen, sehe keinen signifikanten Unterschied bloss dass 'hmpf's Stück in IDEOne nicht kompiliert

Zeile 8

-
Also ich blicke hier nicht mehr ganz durch. Dass der Code von hmpf nicht kompiliert kann ich nachvollziehen. Jedoch kann ich die Ausgabe des Codes von SeppJ nicht ganz nachempfinden.
-
EOutOfResources schrieb:
Also ich blicke hier nicht mehr ganz durch. Dass der Code von hmpf nicht kompiliert kann ich nachvollziehen. Jedoch kann ich die Ausgabe des Codes von SeppJ nicht ganz nachempfinden.
Also wir sind uns hoffentlich einig, dass der Zuweisungsoperator nie zur Wahl stand, genommen zu werden. Denn bei der Erzeugung wird bei dem Gleichheitszeichen das Objekt recht konstruiert und dann eine Kopie gemacht. Der Kopierkonstruktor darf (oder genauer gesagt: muss) hier aber vollständig wegoptimiert werden, selbst wenn er Nebeneffekte hat, da hier ein temporäres Objekt in ein neues kopiert wird. Das ist die berühmte Copy-Elision.
-
Wozu überhaupt erst Dreck machen? In C++ muss, im Gegensatz zu Java, nicht alles auf den Heap. Genaugenommen ist das sogar eher die Ausnahme.
Also wenn möglich sollte ich alles was geht auf den Stack legen oder? Den Stack zu pushen und zu poppen wird schließlich schneller sein, als Speicher am Heap zu de/allokieren oder?
Mit den Sprachelementen von C++ kenne ich mich recht gut aus. Ich weiß aber nicht, wie ich manche Sprachelemente einsetzen soll/muss. Z.B. finde ich das Schlüsselwort typedef recht überflüssig. Warum einem Typen nochmal einen neuen Namen geben? Oder beim Schlüsselwort union kann ich mir nicht vorstellen, dass das in der Praxis gebraucht wird. Oder warum gibt es structs wenn es Klassen gibt?
Ich glaube ich müsste mir deswegen C++ in der Praxis genauer ansehen, damit ich es besser verstehe. Gibt es irgendwo ein paar kleinere Projekte, die aber sauber geschrieben sind?
Oder die noch bessere Frage: Wie habt ihr so gut C++ gelernt??

-
nightrider schrieb:
Also wenn möglich sollte ich alles was geht auf den Stack legen oder? Den Stack zu pushen und zu poppen wird schließlich schneller sein, als Speicher am Heap zu de/allokieren oder?
Technisch: Ja, es ist deutlich schneller. Und semantisch auch klares ja. Das Pushen und Poppen geht automatisch. Bei Heapallokationen musst du selber aufpassen. Der Stack ist so ein bisschen wie eine GC in C++
.Warum einem Typen nochmal einen neuen Namen geben?
1. Schreibfaulheit. Bei komplizierten Templates werden Typennamen schnell mal mehrere Zeilen lang.
2. Wartbarkeit. Es kommt recht oft vor, dass man mal schnell einen Conatinertypen überall ändern möchte. Da alle Container mehr oder weniger gleich angesprochen werden, muss man am Code nichts ändern außer dem Namen. Und da ist es ungemein praktisch, nur eine Stelle zu haben, wo dieser auftaucht.Oder beim Schlüsselwort union kann ich mir nicht vorstellen, dass das in der Praxis gebraucht wird.
Richtig. Ist wegen der Kompatibilität mit C dabei, wird aber recht selten benutzt.
Oder warum gibt es structs wenn es Klassen gibt?
Ebenfalls Kompatibilität mit C. Wird aber durchaus oft benutzt, um den semantischen Unterschied zwischen einer Zusammenfassung von Variablen und einer "richtigen" mit Memberfunktionen die richtig Arbeit verrichten auszudrücken.
-
nightrider schrieb:
Wozu überhaupt erst Dreck machen? In C++ muss, im Gegensatz zu Java, nicht alles auf den Heap. Genaugenommen ist das sogar eher die Ausnahme.
Also wenn möglich sollte ich alles was geht auf den Stack legen oder? Den Stack zu pushen und zu poppen wird schließlich schneller sein, als Speicher am Heap zu de/allokieren oder?
Der Hauptvorteil des Stack ist weniger die Geschwindigkeit, sondern die Sicherheit. Stack-Objekte werden automatisch vernichtet, wenn sie nicht mehr gebraucht werden (egal auf welchem Weg ihr Scope verlassen wurde), Heap-Objekte werden erst zerstört, wenn du explizit delete aufrufst (und wenn du keinen Verweis mehr darauf hast, geht das nicht mehr).
Mit den Sprachelementen von C++ kenne ich mich recht gut aus. Ich weiß aber nicht, wie ich manche Sprachelemente einsetzen soll/muss. Z.B. finde ich das Schlüsselwort typedef recht überflüssig. Warum einem Typen nochmal einen neuen Namen geben? Oder beim Schlüsselwort union kann ich mir nicht vorstellen, dass das in der Praxis gebraucht wird. Oder warum gibt es structs wenn es Klassen gibt?
Erstens hat C++ alle drei Schlüsselwörter aus C geerbt. Zweitens:
* typedef: Die Namen von Datentypen können durchaus etwas umfangreicher werden, da ist es gut, eine Abkürzung dafür definieren zu dürfen. Außerdem ist es praktischer für die Austauschbarkeit, daß z.B. jede Containerklasse die wichtigsten Typen als typedef mitliefert.
* struct: Ist syntaktisch fast äquivalent zu class, aber wird normalerweise verwendet, um einen Unterschied zwischen "echten" Klassen (Datenmember sind privat, Kontakt nach außen erfolgt über öffentliche Methoden) und Wertesammlungen (öffentlich zugängliche Datenmember, eventuell ergänzt um ein paar Hilfsmethoden) zu kennzeichnen
* union: Ist im C++ Umfeld tatsächlich selten (vor allem, weil es sich nicht mit non-POD-Typen verträgt).Oder die noch bessere Frage: Wie habt ihr so gut C++ gelernt??

VIEL Übung und gutes Lehrmaterial.
-
Danke für die super Erklärungen!