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


  • 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...


  • Gesperrt

    Dieser Beitrag wurde gelöscht!

  • Mod

    @JTR666 : Ist ja schön, aber ich werde jetzt sicher keine Literaturanalyse machen. Es fällt bloß auf, dass Leute die hier mit "dummen" Fragen und grundlegend falschen Codes auftauchen, überproportional solche Bücher gelesen haben, wohingegen Leute, die Bücher von der mir empfohlenen Art gelesen haben, eher mit coolen Designfragen kommen, wie sie ihre guten Programme noch besser machen können.


  • Mod

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

    Also normalerweise will man doch Werte kopieren und keine Adressen?

    Dann würde man doch die Pointer dereferenzieren und so die Werte kopieren.

    HeapData(const HeapData &other)
    {
            auto other_data = other.data;
            *data = *other_data;
    }
    

    Bin mir jetzt nicht sicher, wie eine Membervariable direkt dereferenziert wird. Deshalb die zusätzliche Zeile.

    Ähh, du solltest das Kapitel wohl selber wiederholen. Da ist alles falsch. Das würde nur das erste Element eines Feldes kopieren, aber es stürzt sowieso in dem Moment ab, wo du den undefinierten Zeiger data dereferenzierst. Das macht auch keinen Sinn, hier eine temporäre Variable zu nutzen, selbst wenn der Rest richtig wäre, was den Verdacht aufkommen lässt, dass du denkst, dass hier ganz was anderes passiert, als eigentlich passiert.



  • @SeppJ Also nochmal bei 0 anfangen? 😕


Anmelden zum Antworten