Move-Konstruktor



  • @john-0 sagte in Move-Konstruktor:

    Ok, ich habe auf die Schnelle ein paar Dinge verändert, damit das besser passt. Die Standard Frage, weshalb kein std::vector? Und wenn man schon selbst die Rule of Five nutzt, dann die Konstruktoren auch richtig ausformulieren.

    Auf die Schnelle: warum memcpy im copy-Constructor? Das erfordert, dass die Klasse Measure keine speziellen Dinge im Konstruktor tut, keine besitzenden Pointer oder Pointer auf sich selbst enthält usw. Gut, das ist hier der Fall, aber ich schätze mal, dass das hier eh nur ein Toy-Example ist. Zumindest so ganz ohne Warnung wollte ich das nicht einfach so stehen lassen.

    Ansonsten an @C-Sepp: du solltest dir mal hier: https://en.cppreference.com/w/cpp/language/exceptions den Abschnitt zu "Exception safety" durchlesen. Du bietest keine Garantien. Durch Dinge wie copy & move von @john-0 sieht das besser aus. Lies dir vielleicht auch mal Doku zu https://en.cppreference.com/w/cpp/language/noexcept_spec durch.

    Und dann verstehe ich die append-Methode nicht. Weder bei @C-Sepp noch bei @john-0:

    void MeasureArr::append(const Measure& rd)
    {
    	if (anz + 1 <= max)
    		msPtr[anz + 1] = rd;
    }
    

    Würde man nicht erwarten, dass sich der interne anz-Wert um 1 erhöht? Würde man nicht auch erwarten, dass im Fall anz == max (besser anz < max testen als anz + 1 <= max!) irgendwas sinnvolles passiert?



  • Max steht für die maximale Anzahl an Vektorelementen, anz für die aktuelle Anzahl. Die Funktion append() soll einen Messwert hinter den vorhandenen Elementen im Vektor einfügen. Richtiger wäre sicherlich:

    void MeasureArr::append(const Measure& rd)
    {
    	if (anz + 1 <= max)
    		msPtr[anz++] = rd;
    }
    

    Das Objekt rd vom Typ Measure wird in dieser Zuweisung ja den Zeiger msPtr, welcher ebenfalls vom Typ Measure ist, zugewiesen. Warum wird in dem Fall weder der Zuweisungsoperator = noch die Move-Zuweisung ausgeführt?
    Woher weis ich, wann die Move-Zuweisung, wann der Zuweisungsoperator = ausgeführt wird? Wird die Move-Zuweisung manchmal nur für R-Werte ausgeführt. Nochmals vielen Dank!



  • @C-Sepp

    Zeile 3 ist ungewöhnlich, stattdessen besser

    void MeasureArr::append(const Measure& rd)
    {
      if (anz < max)
          msPtr[anz++] = rd;
    }
    

    Hier ist ein Proposal zur move-Semantik, da steht auch drin, wann move greift.



  • @wob sagte in Move-Konstruktor:

    Auf die Schnelle: warum memcpy im copy-Constructor?

    Das geht halt nur im Falle von PODs, was hier der Fall ist.

    Und dann verstehe ich die append-Methode nicht. Weder bei @C-Sepp noch bei @john-0:

    Das hatte ich übersehen.



  • Dieser Beitrag wurde gelöscht!


  • @john-0 sagte in Move-Konstruktor:

    Das geht halt nur im Falle von PODs, was hier der Fall ist.

    Ich würde trotzdem vor dem memcpy folgendes einfügen:

    static_assert(std::is_trivially_copyable<Measure>::value, "Measure must be trivially copyable!");
    

    Man sieht der Klasse Measure nämlich nicht an, das sie trivially copyable sein muss. Und zu schnell hat man die Klasse um ein nicht POD Objekt (z.B. std::string) erweitert.

    Deswegen der static_assert. So klopft einem der Compiler auf die Finger falls Measure nicht trivially copyable ist und deswegen memcpy ein UB zurückliefert.



  • Kann man das nicht dem Compiler überlassen? Der kennt doch den Datentyp und ich habe schon Gerüchte gehört, dass der Compiler automatisch die optimale Kopiermethode auswählt.
    Man könnte im Compiler Explorer mal sehen, welchen Code welcher Compiler erzeugt.



  • @Quiche-Lorraine sagte in Move-Konstruktor:

    Ich würde trotzdem vor dem memcpy folgendes einfügen:

    Dann lieber so

    MeasureArr::MeasureArr (const MeasureArr& msAr) : msPtr(new Measure[msAr.max]), max(msAr.max), anz(msAr.anz)  {
    	std::cout << "Kopierkonstruktor\n";
    	std::copy_n (msAr.msPtr, anz, msPtr);
    }
    


  • Vielen Dank...damit kommt mit Sicherheit so schnell keine Langeweile auf :D.
    Welche Frage sich jetzt noch ergeben hat:
    Warum wird in der Funktion append() den dynamischen Element msPtr[] eigentlich nicht mit new das Objekt rd zugewiesen,
    so wie es auch im Konstruktor gemacht wurde? (msPtr[] = new rd) Danke!



  • Vergleiche mal beide Datentypen bzw. schau dir mal deinen eigenen Kopierkonstruktor oder operator = an...



  • @C-Sepp sagte in Move-Konstruktor:

    Warum wird in der Funktion append() den dynamischen Element msPtr[] eigentlich nicht mit new das Objekt rd zugewiesen, so wie es auch im Konstruktor gemacht wurde? (msPtr[] = new rd) Danke!

    Nein, aus gutem Grund hatte ich den Konstruktor verändert.

    MeasureArr(const size_t mx) : msPtr(new Measure[mx]), max(mx), anz(0) {}
    

    Es wird hier dem Pointer msPtr der Wert aus der Allokation new Measure[mx] zugewiesen. Es wird hier keinerlei Objekt des Typs Measure zugewiesen, sondern nur Speicher für die Anzahl mx an Measure Objekte angefordert. Später kann man dann mit append Measure Objekte in dem bereits allozierten Speicher ablegen.



  • @john-0 sagte in Move-Konstruktor:

    Es wird hier keinerlei Objekt des Typs Measure zugewiesen, sondern nur Speicher für die Anzahl mx an Measure Objekte angefordert.

    Wie kommst du darauf?
    Es wird hier ein Array erzeugt und für jedes Array-Element dessen Standardkonstruktor (hier also Measure()) aufgerufen.

    PS: In deinem Code in Zeile 23 fehlen Kommentarzeichen.



  • Wusst ich's doch. Analog zu string*pStr = new string(20,'X'); könnte man nämlich auch
    msPtr = new Measure(1, 2); in der Funktion schreiben. Jetzt stellt sich nur noch die Frage
    warum bei msPtr[1] = new Measure(1, 2); immer eine Fehlermeldung kommt?



  • Welchen Datentyp hat msPtr und welchen msPtr[i]?

    Außerdem mußt du zwischen statischem und dynamischen Datentyp (zur Laufzeit) unterscheiden.



  • @Th69 sagte in Move-Konstruktor:

    Wie kommst du darauf?

    In append wird ein Wert als Parameter übergeben im Konstruktor nicht.



  • Ah stimmt...msPtr ist ja vom Typ Zeiger auf Measure, msPtr[i] spricht wiederrum den Wert an auf den msPtr[i] zeigt.



  • Wie bereits erwähnt, wird mit msPtr[i] = rd ja ein Wert an msPtr übergeben. Welcher Wert ist das denn...eigentlich wird doch ein Objekt als Parameter übergeben? Wenn das der Wert des Membervariablen des üergebenen Objektes ist, wie verhält sich das dann für mehrere Membervariablen? Danke!



  • @C-Sepp sagte in Move-Konstruktor:

    Ah stimmt...msPtr ist ja vom Typ Zeiger auf Measure, msPtr[i] spricht wiederrum den Wert an auf den msPtr[i] zeigt.

    Der erste Teilsatz stimmt, der zweite nicht:
    msPtr[i] zeigt nicht auf irgendetwas, sondern es beinhaltet ein Measure-Objekt!

    Also wird bei msPtr[i] = rd einfach das Measure-Objekt rd an den Index i des Arrays msPtr kopiert (bedenke, daß msPtr[i] <=> *(msPtr+i)).



  • Warum eine Zeigervariable jetzt ein Objekt beinhalten soll/kann ist mir nicht klar. Eigentlich verweist ein/dieser Zeiger
    doch auf einen Speicherbereich/Array von Objekten vom Typ Measure.
    Jetzt ist mir auch wieder nicht klar, warum man msPtr[i] = new Measure(1,2) nicht schreiben kann?
    msPtr und msPtr[i] sind doch beide vom Typ Zeiger auf Measure, wobei ich mich mit msPtr[i]/ *(msPtr+i) in der Werteebene bewege bzw. mit msPtr der Zeigervariablen einen bestimmten Speicherbereich zuweisen kann.



  • @C-Sepp sagte in Move-Konstruktor:

    msPtr und msPtr[i] sind doch beide vom Typ Zeiger auf Measure,

    Nein, ich habe meinen ursprünglichen Code korrigiert und im Konstruktor einen Aufruf eingebaut, der die Namen der Typen ausgibt. Falls man g++ nutzt werden die Namen in lesbarer Form ausgegeben, bei anderen Compiler hängt es von deren Vorgaben gab.

    P.S. Die Forensoftware missinterpretiert .cc als Toplevel Domain.


Anmelden zum Antworten