Kann mir jemand eben erklären, wie die Kopierkonstruktoren mit Referenzparameter funktionieren?



  • Ich meinte auch HeapData^^
    War nur unkonzentriert und hab dann aus Versehen Schiff geschrieben.^^



  • Könntest du den Code hier bitte selber formatieren (damit u.a. die Zeilenangaben auch stimmen)?

    Zuerst einmal, du meinst sicherlich (in der main()).

    HeapData hd;
    
    HeapData other = hd;
    

    Im Kopierkonstruktor bezieht sich der Parameter other auf den übergebenen Wert (HeapData other = hd; ist semantisch äquivalent zu HeapData other(hd);), also auf hd.

    Und bei HeapData hd wird der Standard-Konstruktor (default constructor) HeapData() aufgerufen und dort wird dieses Objekt initialisiert (d.h. ein char-Array mit 27 Elementen auf dem Heap (im C++ Standard 'Freispeicher' genannt) erzeugt).
    Und deswegen kann dieses Objekt hd dann als Referenz an das andere zu erzeugende Objekt other weitergegeben werden (ich gebe dir Recht, daß die zweimalige - und dann noch unterschiedliche - Verwendung von other hier sehr unglücklich ist; daran sieht man, daß gerade Internet-Tutorials nicht immer von Pädagogen entworfen wurde!).

    Und beide Objekte (Instanzen) leben bis zum Ende des Blocks (der main()-Funktion) und dann wird für beide (in umgekehrter Reihenfolge) jeweils deren Destruktor ~HeapData() aufgerufen (und dann gilt, was @wob geschrieben hat - doppelte Freigabe des gleichen Speichers).



  • @JTR666
    Tu dir selbst einen Gefallen und schaue dir erst einmal die STL an.

    https://en.cppreference.com/w/cpp/container

    Wenn ich HeapData schon lese, weckt das in mir unangenehme Assoziationen.



  • @Quiche-Lorraine Ich hab nur den Quellcode aus dem Video abgeschrieben^^


  • Mod

    Unlesbare Formatierung 👎 Ich zitiere es mir noch einmal selber, um Zeilennummern für meine Antwort zu haben.

    class Schiff
    {
    public:
        HeapData()
        {
            data = new char[27]
        }
    
        HeapData(const HeapData &other)
        {
            data = other.data;
        }
    
        ~HeapData()
        {
            delete[] data;
        }
    
    private:
        char *data;
    };
    
    int main()
    {
        HeapData hd;
    
        HeapData other = data;
    
    
        system("PAUSE");
    
        return 0;
    }
    

    Von welcher Variablen bezieht der Parameter (also die Referenz) seine Adresse?

    Das ist das Objekt, das kopiert werden soll. Kopierkonstruktoraufrufe können so aussehen:

    MeineKlasse meine_kopie(mein_original);
    

    oder so (siehe auch nächste Frage):

    MeineKlasse meine_kopie = mein_original;
    

    Letzteres sieht zwar aus wie eine Zuweisung, ist aber bei Definition eines neuen Objekts stattdessen ein Aufruf des Kopierkonstruktors. In beiden Fällen hätte würde sich das Argument des Kopierkonstrukotrs auf mein_original beziehen, das aktuelle Objekt wäre hingegen meine_kopie

    Warum hat er in Zeile 11 data = other.data geschrieben?

    Hoffentlich, weil er zeigen möchte, wie es nicht geht. Falls er das ernst meint, darfst du nicht weiter von ihm lernen. Ernsthaft. Die nächste Lektion muss eine Erklärung sein, wieso man das nicht so machen darf. Das sieht man sogar jetzt schon, wenn man mal die ganzen Syntaxfehler aus dem Programm entfernt und es ausführt:
    https://ideone.com/zhHYcN
    Bumm! Stürzt ab!

    Welches other ist das? Das der Main oder der Referent?
    

    Die Referenz. Funktionen können nicht in andere Funktionen gucken. Das other in main ist ein ganz anderes other, der Name ist nur Zufall.

    Denn ich kenne diese Schreibweise nur, wenn man aus der main-Funktion mit einer Variablen des Klassentyps eine
    Instanz der Klasse einen Wert zuweisen möchte. (Also sowas wie HeapData fish; fish.fishstick = 42) wenn es die Public-
    Membervariable fishstick geben würde und diese int wäre.) 
    

    Du musst erst noch einmal wiederholen, wie Klassen und Membervariablen funktionieren. Dir fehlen wichtige Grundlagen, die dich hindern die eigentliche Lektion zu lernen, die er hier lehren möchte. Du musst in der Lage sein, solchen Code zu lesen, sonst kann man nicht erklären, wieso er das so schreibt.

    Woher bezieht in der main die Variable hd ihren Wert? Schließlich geben die Konstruktoren nichts aus.

    Er ruft den Konstruktor aus Zeile 4 (in meinem zitierten Code) auf.

    Und zu guter letzt: Der Heap wird ja beim Verlassen des Konstruktors nicht gelöscht, weil das char[27] ja nicht auf dem Stack liegt. Ergo gibt es die Daten noch, wenn im Kopierkonstruktor data aufgerufen wird. Deswegen kann other, weil Referenz, auf den Pointer *data zugreifen (falls bei other.data das data der Parameter ist). Bezieht dann der Parameter seine Adresse von hd in der Main?

    Ich weiß nicht, ob du das richtige meinst, aber es klingt so, als wärst du dem Problem auf der Spur. Das Problem hier ist jedenfalls (siehe abstürzender Code oben), dass sich hd und other am Ende ein und den gleichen Zeiger auf das char[27] (ursprünglich von hd) teilen. Und wenn der Stack abgeräumt wird, rufen beide in ihrem Destruktor delete darauf auf. Und dann kracht es, weil man nur genau einmal delete pro new aufrufen darf (und muss).
    Eine Erklärung von Kopierkonstruktoren sollte eigentlich erklären, wie man das verhindert, aber es macht es genau falsch.

    Und zu guter Letzt: Du wirst das nie brauchen. In modernem C++ wird man nie new oder delete benutzen, außer man schreibt irgendwelche low-level Libraries zu Speichermanagement. Man wird auch quasi nie Kopierkonstruktoren oder Zuweisungsoperatoren selber schreiben, außer einmalig zur Übung oder wenn man low-level Libraries zu Smartpointern schreibt. Das ganze Wissen hier dient bestenfalls zur Erklärung, was besagte low-level Libraries intern machen, und wieso man ausschließlich diese benutzen sollte. Denn die machen das richtig. Im Gegensatz zu der falschen Erklärung hier.



  • @Th69 Also weist das hd dadurch, dass es vom Typ HeapData ist, automatisch nach Beendigung des Default-Konstruktors auch auf den Heap (beinhaltet also die Adresse vom Heap)? Ich frag mich nur wie man sich das grad am besten vorstellen soll^^
    Dann schreibt er ja other = heap, wodurch die Adresse die in Heap gespeichert ist an other übergeben wird. Direkt danach springt der Kopierkonstruktor an, &other verweist dann auf hd, oder halt auf die den Heap, weil selbe Adresse.
    Nur warum schreibt er other.data? Das macht für mich einfach überhaupt keinen Sinn^^



  • @SeppJ Er meinte auch, dass man das so nicht machen darf und wollte zeigen, dass es dann zum Absturz kommt.^^ Deswegen ist es so offensichtlich richtig gewesen, wie er das gemacht hat.
    Also dann jetzt noch mal Butter bei die Fische: Wie formatiert man hier den Quellcode richtig?
    Dann kann ich das eben bearbeiten! 🙂



  • Das mit den Zeigern ist, wie schon von uns geschrieben, ein sehr schlechtes Beispiel.
    Stell es dir besser mit einer einfachen Zahl vor, z.B.

    class heapData
    {
    public:
      HeapData()
      {
        data = 42;
      }
    
      HeapData(const HeapData &other)
      {
        data = other.data;
      }
    
    private:
      int data;
    };
    

    Der Sinn eines Kopierkonstruktor besteht eben dadrin, aus einem übergebenen Objekt eine Kopie anzulegen, d.h. die Memberwerte zu übernehmen (wobei Sinn macht dieses Beispiel nur, wenn sich der Datenwert auch zur Laufzeit ändern kann).
    Aber bei den meisten Klassen brauchst du keinen eigenen Kopierkonstruktor, sondern beim Kompilieren wird automatisch ein passender Kopierkonstruktor erzeugt oder du schreibst explizit:

    HeapData(const HeapData &other) = default;
    

    bzw. wenn du ihn nicht haben möchtest:

    HeapData(const HeapData &other) = delete;
    

    PS: Editieren geht bei einem Beitrag unten rechts mit dem Menü mit den drei senkrechten Punkten und dort dann "Bearbeiten" auswählen.



  • Welches Video ist das denn? Du könntest ja mal ein Link reinstellen, dann könnte man sich mal angucken, ob das was taugt.



  • @SeppJ Also nochmal wieder im C++ für Dummies mit dem kompletten Kapitel Klassen anfangen? uff 😃




  • Mod

    @JTR666 sagte in Kann mir jemand eben erklären, wie die Kopierkonstruktoren mit Referenzparameter funktionieren?:

    @SeppJ Also nochmal wieder im C++ für Dummies mit dem kompletten Kapitel Klassen anfangen? uff 😃

    Wäre wahrscheinlich besser, gar nicht solche Bücher zu lesen. C++ ist nicht einfach. Bücher die schnellen ("in 14 Tagen"), einfachen ("für Dummies"), oder vollkommenen ("von A bis Z") Lehrerfolg versprechen, lügen. Diese Bücher fallen hier im Forum auch ständig negativ auf, dass sie Dinge falsch beibringen, veraltetes darstellen, und schlechte Praktiken lehren.

    Gute Bücherliste:
    https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282



  • @JTR666
    Das letzte mal als ich eine HeapData Implementierung gesehen habe, hat sich der Verursacher so richtig in Knie geschossen. Aber mit Kanonenkugeln.

    Lass bitte anfangs die Finger weg von new, delete, malloc, free! Manuelle Speicherverwaltung ist nicht einfach. Nimm STL!

    Und wie mein Vorredner sagte, schnapp dir ein gutes Buch und arbeite es durch.



  • @SeppJ Das von Bjarne Stourstroup hab ich sogar hier^^ Nur wollte ich eigentlich zuerst mit dem einen Buch fertig werden und dann das nächste lesen.
    Das Problem ist halt nur, dass jedes Buch anders aufgebaut ist.
    Wenn in Buch A zuerst das eine und dann das andere Thema behandelt wird, kann es sein, dass das nächste Buch zuerst noch n anderes Thema, dann das andere und dann das eine Thema behandelt. Deswegen es sein kann, dass man für Antworten die Quellcodes aus dem nächsten Buch um sich Thema A beizubringen nicht verstehen kann, weil man dafür eventuell noch was wissen muss, was man noch nicht gelernt hat...
    Verstehst du was ich meine?



  • @JTR666 Möglicherweise hat die andere Reihenfolge in einem guten Buch ja ihren Grund ...



  • @manni66
    Das habe ich selbstverständlich nicht bestritten! 🙂



  • @Quiche-Lorraine Was hat er gemacht? 😮



  • Warum hat er in Zeile 11 data = other.data geschrieben?

    Wenn du dem Sprecher im Video zugehört hättest, machst er das, um noch einmal deutlicher zu machen, was der Default-Kopierkonstruktor tut und warum das Programm einen Fehler verursacht. Das ganze Video hat doch genau den Sinn, auf die Dreierregel hinzuweisen.

    Gut, am Ende empfiehlt der Videomensch, den Kopierkonstruktor einfach zu löschen, soband man Pointer hat. Auch ne Lösung... Aber einfach vector statt char* verwenden und alles wäre automatisch richtig.



  • Dieser Beitrag wurde gelöscht!


  • @SeppJ Also in dem für Dummies-Buch ist die Reihenfolge so, dass man zuerst grundlegend lernt wie man eine Ausgabe macht, dann wie man sein System einrichtet.
    Dann im 2. Kapitel, wie man ein Objekt erstellt (Compiler Code::Blocks).
    Im 3. Kapitel wie man Daten speichert (also was Variablen sind, welche Typen es gibt, usw.)
    Im 4. Kapitel lernt man was Schleifen und if/else sind.
    Das nächste Kapitel bezieht sich dann darauf, wie man sein Werk mit Funktionen aufteilt.
    Dann kommt im 6. Kapitel das Aufteilen des Quellcodes dran.
    Gefolgt vom 7. Kapitel, welches sich mit Pointern befasst.
    Und dann im 8. Kapitel kommen Klassen dran.
    Insgesamt gibt es 35 Kapitel auf 820 Seiten...


Anmelden zum Antworten