Objekt kopieren *this Zeiger



  • Hallo,
    ich lese gerade das Buch C++ von A bis Z. Hier mal eine Methode, die ein Objekt kopieren soll:

    void Mensch:kopie_Mensch(Mensch *p) {
    Mensch tmp = *this;
    *p=tmp;
    }
    

    Wieso kann man nicht gleich schreiben:
    *p=*this?

    Viele Grüße



  • Das hat was mit Exceptionssicherheit zu tun.

    Wenn jetzt das neu erstellen eines Objektes fehl schlägt, dann hat das keinerlei Auswirkung auf das Objekt, was dir strenge Exceptionsicherheit gibt. Das komplexe ist ja das erstellen eines Objektes und das kann vielleicht fehl schlagen. Darum wird ein temporäres Objekt erstellt.
    Damit das aber geht, geht man davon aus, dass eben diese Zuweisung ebenfalls die strenge Garantie gibt, was meist durch ein swap realisiert wird, welches selbst die strenge Garantie geben soll. Das ganze nennt sich dann Copy/Swap Idiom.



  • drakon schrieb:

    Das hat was mit Exceptionssicherheit zu tun.

    Wenn jetzt das neu erstellen eines Objektes fehl schlägt, dann hat das keinerlei Auswirkung auf das Objekt, was dir strenge Exceptionsicherheit gibt. Das komplexe ist ja das erstellen eines Objektes und das kann vielleicht fehl schlagen. Darum wird ein temporäres Objekt erstellt.
    Damit das aber geht, geht man davon aus, dass eben diese Zuweisung ebenfalls die strenge Garantie gibt, was meist durch ein swap realisiert wird, welches selbst die strenge Garantie geben soll. Das ganze nennt sich dann Copy/Swap Idiom.

    Gute Begründung, aber ist das nicht die Begründung für

    void Mensch:kopie_Mensch(Mensch *p) { 
      Mensch tmp = *this; 
      swap(*p,tmp);
    }
    

    ?



  • drakon schrieb:

    ...Wenn jetzt das neu erstellen eines Objektes fehl schlägt, ...

    😕

    void Mensch:kopie_Mensch(Mensch *p) {
      *p=*this;
    }
    

    😕
    Wo wird denn hier ein neues Objekt erstellt ?



  • Ehm ja. Ich habe da nur was von kopieren gelesen und den Code kurz gesehen und dann wars schon um mich geschehen, weil das sonst so eine Standardding ist. 🙂

    Imo ist diese Funktion unnötig. Wenn man eine Kopie will, dann hat man dafür Copy Konstruktor oder aber eben den Zuweisungsoperator (den ich fälschlicherweise zuerst sehen wollte), wo dann das Copy/Swap Idiom vor kommt.

    /EDIT
    Wenn diese Funktion nicht überflüssig wäre und man nicht den von dir gebrachten Vorschlag machen könnte, dann wäre jegliche Zuweisung mittels des =-Operators (Was dein Vorschlag ja genau ist) falsch, was ja definitiv nicht so ist..



  • Also die Methode soll ja kein Objekt erstellen, sondern den Inhalt eines Objektes in ein anderes kopieren.



  • Johnas G. schrieb:

    Also die Methode soll ja kein Objekt erstellen, sondern den Inhalt eines Objektes in ein anderes kopieren.

    Jap. Und das ist genau die Aufgabe des = Operators. 😉
    Der wirkt dann aber normalerweise auf das aktuelle Objekt. Das heisst, dass das andere andere Objekt demjenigen übergeben wird, welches die Daten annehmen soll. Ist imo auch einiges intuitiver, als umgekehrt.



  • Ja. Das ist mir schon klar. Das wird auch ein paar Seiten weiter erläutert. Bei dem Beispiel ging es nur darum, Methoden mit Objekten als Parametern zu demonstrieren.

    Aber wieso ist diese Variante jetzt unsicher:

    void Mensch:kopie_Mensch(Mensch *p) {
      *p=*this;
    }
    

    Es wird ja gar kein Objekt erstellt.



  • Es ist unsicher, weil während der Zuweisung (von *p) eine Exception auftreten kann, welche eine inkonsistentes Objekt (*p) zur Folge haben kann.

    Das wird, wie von anderen gesagt, mit dem Copy-Swap Idiom gelöst.

    Simon



  • theta schrieb:

    Es ist unsicher, weil während der Zuweisung (von *p) eine Exception auftreten kann, welche eine inkonsistentes Objekt (*p) zur Folge haben kann.

    Das wird, wie von anderen gesagt, mit dem Copy-Swap Idiom gelöst.

    Simon

    Die Begründung macht aber höchstens dann Sinn, wenn Pointer auf Heapobjekte geswapt werden. Wenn hier operator= eine Exception wirft, kann das auch erst bei der zweiten Zuweisung passieren. Damit wäre also nichts gewonnen.

    Lars



  • manni66 schrieb:

    theta schrieb:

    Es ist unsicher, weil während der Zuweisung (von *p) eine Exception auftreten kann, welche eine inkonsistentes Objekt (*p) zur Folge haben kann.

    Das wird, wie von anderen gesagt, mit dem Copy-Swap Idiom gelöst.

    Simon

    Die Begründung macht aber höchstens dann Sinn, wenn Pointer auf Heapobjekte geswapt werden. Wenn hier operator= eine Exception wirft, kann das auch erst bei der zweiten Zuweisung passieren. Damit wäre also nichts gewonnen.

    Ja. Das ist aber per Definition der Exceptionsicherheit kein Problem.
    Kennst du die Definition überhaupt? - Exceptionsicherheit hats nichts damit zu tun, dass keine Exception geworfen werden darf, sondern lediglich, wie konsistent sich das System verhält, falls eine geworfen wird.



  • drakon schrieb:

    manni66 schrieb:

    theta schrieb:

    Es ist unsicher, weil während der Zuweisung (von *p) eine Exception auftreten kann, welche eine inkonsistentes Objekt (*p) zur Folge haben kann.

    Das wird, wie von anderen gesagt, mit dem Copy-Swap Idiom gelöst.

    Simon

    Die Begründung macht aber höchstens dann Sinn, wenn Pointer auf Heapobjekte geswapt werden. Wenn hier operator= eine Exception wirft, kann das auch erst bei der zweiten Zuweisung passieren. Damit wäre also nichts gewonnen.

    Ja. Das ist aber per Definition der Exceptionsicherheit kein Problem.
    Kennst du die Definition überhaupt? - Exceptionsicherheit hats nichts damit zu tun, dass keine Exception geworfen werden darf, sondern lediglich, wie konsistent sich das System verhält, falls eine geworfen wird.

    Wo ist der Unterschied bezüglich Exceptionsicherheit, wenn die Zuweisung über ein Zwischenobjekt läuft? *p wird statt durch das Original jetzt durch die Kopie verändert. Sollte dabei aber eine Exception geworfen werden, kann *p genauso in einen inkonsistenten Zustand geraten. Können dagegen Pointer auf die eigentlichen Objekte umgesetzt werden erhält man garantiert ein in sich konsistentes Objekt:

    class C;
    C* a = new C;
    C* b = new C;
    
    C* temp = new C;
    *temp = *a;  // wenn Exception bleibt b unverändert
    b = temp;  // b ist jetzt ein konsistentes Objekt
    

    Echter Code müsste natürlich noch die Speicherlecks vermeiden.

    Lars



  • Die Kunst liegt bei dem swap.

    Also. Du verlagerst das Kopieren in ein temporäres Objekt, weil das eben fehl schlagen kann. Dann hast du ein komplettes, offensichtilch intaktes Objekt (falls nicht, dann bleibt alles beim alten).

    Nun kannst du, und das ist die Voraussetzung, mit einem Nicht werfendem swap die Elemente des aktuellen Objektes und des anderen tauschen. Das funktioniert also garantiert.

    Es heisst ja Copy/Swap und nicht Copy/Assign. Da hättest du ja eine endlose Rekursion. Mach dir die Funktionsweise von swap klar und auch warum es so wichtig ist, dass da nichts schief geht. (Im nächsten Standard gibt es dann die move Semantik, welche genau für so etwas da sein wird).



  • Man ist das kompliziert.



  • drakon schrieb:

    Die Kunst liegt bei dem swap.

    Also. Du verlagerst das Kopieren in ein temporäres Objekt, weil das eben fehl schlagen kann. Dann hast du ein komplettes, offensichtilch intaktes Objekt (falls nicht, dann bleibt alles beim alten).

    Nun kannst du, und das ist die Voraussetzung, mit einem Nicht werfendem swap die Elemente des aktuellen Objektes und des anderen tauschen. Das funktioniert also garantiert.

    Es heisst ja Copy/Swap und nicht Copy/Assign. Da hättest du ja eine endlose Rekursion. Mach dir die Funktionsweise von swap klar und auch warum es so wichtig ist, dass da nichts schief geht. (Im nächsten Standard gibt es dann die move Semantik, welche genau für so etwas da sein wird).

    Was du sagst ist richtig und läuft (z.Zt.) schlussendlich auf das swappen von Pointern hinaus.

    Der Code im Originalpost hat damit aber nichts zu tun:

    void Mensch:kopie_Mensch(Mensch *p) {
    Mensch tmp = *this;
    *p=tmp;
    }
    

    enthält nun mal kein swap, sondern zwei Zuweisungen und ist deshalb genauso sicher oder unsicher wie

    void Mensch:kopie_Mensch(Mensch *p) {
    *p=*this;
    }
    

    Auch std::swap ist z.Zt. eher ohne move implementiert und hilft dann nicht weiter.

    Lars



  • Das stimmt natürlich. Wenn du Klassen hast, deren Kopie Exceptions werfen kann und die kein exceptionsicheres Swap anbietet, bringt der ganze Aufwand nichts. Genauso, wenn man unfähig ist, den Zuweisungsoperator mit Copy&Swap zu implementieren.

    Ich bezweifle auch, dass Move-Semantik dem abhelfen soll; schliesslich wird diese vor allem eingesetzt, um temporäre Kopien zu verhindern und ein Objekt gleich am Ziel zu konstruieren. Bei einem Tausch existieren beide Objekte schon vorher, wie soll hier durch Move automatisch ein exceptionsicheres Swap entstehen?



  • manni66 schrieb:

    Was du sagst ist richtig und läuft (z.Zt.) schlussendlich auf das swappen von Pointern hinaus.

    Der Code im Originalpost hat damit aber nichts zu tun:

    Jap. Ich denke wir haben aneinander vorbei geredet. In meinen zweiten Post habe ich gesagt, dass das Beispiel so wirklich anderst geschrieben hätte werden können. Ich habe mich dann auf den die normale Implementierung des Assignments Operators gestützt. Dachte du meinst das.

    /EDIT:
    Klar im Moment ist swap noch ohne move implementiert, aber das ändert sich dann in C++0x, wo das ganze dann darauf umgestellt wird:
    http://www2.research.att.com/~bs/C++0xFAQ.html#rval
    Wegen der Exceptionsicherheit dachte ich ich hätte auch mal was dazu gelesen, dass das ganze erst richtig "sicher" macht. Aber das habe ich jetzt nicht mehr gefunden, kann mir aber auch persönlich gut vorstellen, dass es so ist.


Anmelden zum Antworten