Move Konstruktor Funktionsweise / Vorgehen



  • Alles klar, das mit dem xvalue habe ich mal soweit begriffen, aber was bedeutet "leer"?

    Mit doppelter Eigentümerschaft meist du, dass die Eigenschaften beider Objekte auf die selbe Speicheradresse zielen?



  • mit "leer" meinte ich, dass das (ursprüngliche) objekt sicher zerstört werden kann und nichts enthält (keine zeiger/handles), was mittlerweile anderen objekten gehört (eigentümerschaft).

    und mit eigentümerschaft meine ich: ein eigentümer ist zuständig dafür, eine ressource (dynamischer speicher, dateihandle, thread, etc.) freizugeben. es kann und darf nur einen eigentümer geben.

    ein copy-ctor erzeugt eine kopie - also eine zweite ressource: das ursprüngliche objekt ist weiterhin eigentümer aller seiner ressourcen, das neue objekt erhält neue ressourcen und ist eigentümer davon.

    ein move-ctor "holt" (daher "move") sich die eigentümerschaft vom ursprünglichen objekt. d.h. das ursprüngliche objekt darf nicht mehr eigentümer sein (darf die ressourcen nicht selbst freigeben). eine möglichkeit, das zu tun, ist etwa zeiger auf nullptr zu setzen (weil delete nullptr nichts bewirkt). du kannst dem ursprungsobjekt auch irgendwie anders mitteilen, dass es sich nicht um die freigabe der ressourcen kümmern muss:

    class Moving {
        ressource handle;
        bool owner = true;
    
    public:
        Moving (moving&& other) noexcept : handle{std::move(other.handle)} { other.owner = false; }
    
        ~Moving () {
          if (owner) release_and_free(handle); 
        }
    };
    

    wob schrieb:

    Vielleicht das wichtigste:

    Ein move-Konstruktor, der einfach wie ein Copy-Konstruktor arbeitet, ist ok.

    das halte ich für keinen guten ratschlag... (ich weiß, ist eh nicht so gemeint)

    ein move-konstruktor, der wie ein copy-konstruktor arbeitet ist außerdem nicht exception-safe.



  • kümmern muss kümmern darf.



  • Danke euch beiden für die Antwort.
    Also machen Move-Konstruktoren besonders bei der Verwendung von Zeigern oder Multithreading Sinn, da über sie die Eigentümerschaft einem anderen Objekt zugewiesen und dem Orignal einfach entzogen werden kann?

    In wie fern machen Move-Konstruktoren den bei der Verwendung von Basisdatentypen Sinn?

    class myclass
    {
         int ival;
         double dval;
    public:
         myclass(myclass &&old) : ival(old.ival), dval(old.dval)
         {
              old.ival = 0;
              old.dval = 0.0;
         }
    };
    

    Macht ein Verschiebe-Konstruktor mit solchen Eigenschaften überhaupt Sinn?
    Denn hier gibt es ja keinen Zeiger über welchen die Eigentümerschaft geändert werden kann?

    Danke für eure Gelduld 😃



  • richtig, in deinem beispiel macht ein move-konstruktor überhaupt keinen sinn - und es ist auch völlig unnötig, old.ival und old.dval auf null zu setzen.

    wenn man das einmal versteht, sieht man auch, dass es oft einfach überhaupt unnötig ist, sich um selbst geschriebene move, copy, default konstruktoren und destruktoren zu kümmern.

    class myclass {
       int ival;
       double dval;
       std::string s;
    
    public:
       myclass(myclass &&) = default;
       myclass(myclass const&) = default
       myclass& operator=(myclass const&) = default;
       myclass& operator=(myclass&&) = default; 
    };
    

    siehe auch die rule of three/five/zero

    rule of three (schon in C++98)
    wenn eine klasse eine der drei funktionen: (1) copy ctor (2) copy-op= oder (3) dtor definiert und nicht die default-variante des compilers verwendet, ist es ziemlich sicher nötig, auch die anderen beiden funktionen zu definieren.

    rule of five:
    zusätzlich zu copy ctor, copy-op= und dtor kommen noch move-ctor und move-op= dazu.

    rule of zero:
    für klassen, die keine ressourcen freigeben müssen (eigentümerschaft), sollte man auch keine der fünf funktionen selbst definieren. klassen, die sich um ressourcen kümmern (eigentümerschaft), sollen sich ausschließlich darum kümmern.



  • dove schrieb:

    ein move-konstruktor, der wie ein copy-konstruktor arbeitet ist außerdem nicht exception-safe.

    So generell ist das falsch. Gegenbeispiel ist eine Klasse mit nur einer int-Variablen als Member, die sonst nix tut. Aber klar, so war das nicht gemeint 😉

    Inmeining schrieb:

    In wie fern machen Move-Konstruktoren den bei der Verwendung von Basisdatentypen Sinn?

    Ist völlig sinnlos, da move in diesem Fall nix anderes tut als kopieren.
    (nen pointer ist auch ein Basisdatentyp, aber den meintest du nicht, richtig?)



  • wob schrieb:

    Inmeining schrieb:

    In wie fern machen Move-Konstruktoren den bei der Verwendung von Basisdatentypen Sinn?

    Ist völlig sinnlos, da move in diesem Fall nix anderes tut als kopieren.
    (nen pointer ist auch ein Basisdatentyp, aber den meintest du nicht, richtig?)

    ich weiß nicht, was ihr damit genau meint.

    dass eine basisklasse einen move-konstruktor zur verfügung stellt, kann schon sinnvoll sein. std::basic_ostream macht das z.b.



  • Ich hatte das so verstanden, dass mit "Basisdatentyp" sowas "POD-artiges ohne Pointer" gemeint war (das Beispiel hatte ja nur int und double).

    Glaube nicht, dass Inmeining damit ne Basisklasse gemeint hatte (was du offenbar daraus verstanden hast). Wenn doch, dass ergibt meine Antwort natürlich keinen Sinn.



  • jo, ich hatte zuerst auch überhaupt schwierigkeiten, nachzuvollziehen, auf was sich dein zitat bezogen hat, wohl weil Inmeining das noch nachträglich editiert hat...



  • Danke für eure Antworten, es ist mir soweit glaube ich nun klar.
    Ja ich meinte eine Klasse mit Basisdatentypen.
    Was mich nicht ganz klar war, war dass es erst bei Klassen Sinvoll ist, die bspw. Zeiger oder Multithreading enthalten.


Anmelden zum Antworten