Kopierkonstruktor, Zuweisungsoperator



  • was mir so auf anhieb auffaellt :

    Dynamic& operator=(const Dynamic& rhs);
    

    besser:
    const Dynamic& operator=(const Dynamic& rhs);
    = operatoren willst ja hoffentlich ned verketten 🙂

    Das mit der uniqueObjId scheint mir rein logisch fragwuerdig ...
    warum braucht man die ? iss es richtig, das kopien die gleiche uniqueObjId haben ?
    Instanzen selber kann man meist gut ueber ihre Adresse auseinanderhalten 🙂

    Dynamic obj1(10,10), obj2;
    

    obj1 ok, obj2 ? du hasst kein standard Construktor definiert, wie soll sich das genau verhalten ....

    dein Hauptproblem iss aber das was camper schreibt ...

    Ciao ...


  • Mod

    RHBaum schrieb:

    was mir so auf anhieb auffaellt :

    Dynamic& operator=(const Dynamic& rhs);
    

    besser:
    const Dynamic& operator=(const Dynamic& rhs);
    = operatoren willst ja hoffentlich ned verketten 🙂 [/cpp]

    wieso nicht ? welche sicherheit gibt dieses zusätzlich const? im übrigen gibt der operator für built-in typen ebenfalls non-const lvalues zurück.

    Dynamic obj1(10,10), obj2;
    

    obj1 ok, obj2 ? du hasst kein standard Construktor definiert, wie soll sich das genau verhalten ....

    Dynamic(int val=0, int size = 0);
    

    ist doch ein schöner default konstruktor...



  • Dynamic(int val=0, int size = 0);
    ist doch ein schöner default konstruktor...

    Ups, die default-values uebersehen, stimmt

    wieso nicht ? welche sicherheit gibt dieses zusätzlich const? im übrigen gibt der operator für built-in typen ebenfalls non-const lvalues zurück.

    nicht wirklich einen, weil selbst bei der normalen verkettung der compiler das anders aufloesst ....

    x = y = z; ist kein problem oder ?

    aber:
    (x = y) = z;
    Was macht das fuern sinn ? woelltest du das compilieren koennen ?

    Ciao ...



  • camper schrieb:

    arraySize = rhs.arraySize;
    

    hier wird arr nicht reallokiert. schlecht wenn rhs.arraySize grösser als der verherige wert von arraySize war...

    Ich versteh nicht so richtig was mit dieser Zeile nicht in Ordnung ist. Könnt ihr mir das vielleicht ein bißchen genauer erklären?



  • Es ist so wie camper schon geasgt hat. Du gibst arraySize einen neuen wert, änderst aber die grösse von arr nicht. Eigentlich müsstest du arr zuerst freigeben und dann mit der neuen Grösse neu anlegen.
    Kurt



  • Wäre das so korrekt?

    Dynamic& Dynamic::operator=(const Dynamic& rhs)
    {
          if(this == &rhs)
             return *this;
    
          value = rhs.value;
          arraySize = rhs.arraySize;
    
          delete[]arr;
          arr = new int[arraySize];
    
          for(int i=0; i<arraySize; i++)
          {
              arr[i] = rhs.arr[i];
          } 
    
          id=rhs.uniqueObjId;
    
          return *this;
    }
    


  • So sollte es funktionieren.
    Natürlich musst du das array nicht löschen und neu anlegen wenn
    rhs.arraySize <= arraySize. Finde es aber besser so.
    Kurt



  • RHBaum schrieb:

    aber:
    (x = y) = z;
    Was macht das fuern sinn ?

    Keinen wirklichen. Wer macht aber auch sowas?

    RHBaum schrieb:

    woelltest du das compilieren koennen ?

    Durchaus. Mit primitiven Typen funktioniert das ja auch.

    Sofern es keinen trifftigen Grund gibt, sollte op= imo foo& zurückgeben, und nicht const foo&. Damit ist das Handling intuitiv so wie bei primitiven Typen.



  • warum wird eigentlich zweimal der Destruktor aufgerufen?



  • Damit ist das Handling intuitiv so wie bei primitiven Typen.

    Vielleicht isses ja meine rebellische Natur 🙂 aber grad bei den zuweisungsoperatoren find ich es uberhaupt nicht intuitiv .... liegt sicher auch daran, dass die faelle , wo es ueberhaupt greifen wuerde, soweiso keiner "macht"

    bei den anderen binaeren verkettenbaren operatoren hasst natuerlich vollkommen recht ...

    Nur weil es bei den Pods geht, soll es mit meinen Klassen auch gehen, obwohls keinen Sinn macht, find ich irgendwie nicht recht nen gutes Argument

    und wenn wer wirklich schreibt (x=y)=Z halt ich das fuer nen Denk oder tippfehler, wo der compiler ruhig maulen sollte ...

    @GR_Watto
    Siehe deinen anderen Thread ... 🙂


  • Mod

    wer (x=y)=z schreibt, ist auch bereit mal ein const_cast zu schreiben. im übrigen könnte ein x=y=z denselben effekt haben, falls x bzw. y mal ein alias für y bzw. x ist. und dann hilft dir dein const nicht weiter. ich halte diese variante aber generell für wahrscheinlicher. im übrigen ist (x=y)=z ja nur undefiniert, falls x einen trivialen zuweisungsoperator hat (andernfalls befinden sich ja im operator bereits sequence points).



  • wer (x=y)=z schreibt, ist auch bereit mal ein const_cast zu schreiben

    wer sowas absichtlich macht ... ja. wie gesagt, ich wuerd immer aufn schreibfehler tippen ...
    Genau so wie jemand nen rueckabewert einer Zuweisung weiterverwenden sollte ... macht eigentlich kein sinn.
    Das const bringt an der stelle nicht viel. macht aber auch nix kaputt.
    Bei mir greift an der stelle eben nur die interne logic ...
    soll der anwender meines operators auf den rueckgabewert schreiben koennen ?
    noe ... in 99.99999999999999999 % der Faelle wertet er den gar nicht aus ...
    wenn doch, und er gleich wieder drueberschreibt, isses logisch sehr zweifelhaft
    also mach ma den mal const ... dann hats wenigstens die chance das beim "schreibfehler" der compiler mault.

    Nenn mir nen beispiel wo genau das const einer intuitiven Verwendung entgegen wirkt, dann werd ichs mir auch wieder abgewoehnen 🙂

    Ciao ...



  • RHBaum schrieb:

    Genau so wie jemand nen rueckabewert einer Zuweisung weiterverwenden sollte ... macht eigentlich kein sinn.

    Also das kann ich nicht unterschreiben. Code wie dieser macht mir immer Bauchweh.

    int sz_read = read(..);
    while ( 0 != sz_read ) {
        ....    
        .....        // und am ende dann wieder
        sz_read = read(..);
    }
    

    Finde es einfach kürzer logischer besser und vor allem weniger Fehleranfällig

    while ((sz_read = read(..)) != 0) {
        ....    
    }
    

    Kurt



  • Also das kann ich nicht unterschreiben. Code wie dieser macht mir immer Bauchweh.

    Ok hasst recht, ich berichtige:

    Macht keinen Sinn, auf den Rueckabewert einer zuweisung zu schreiben ...

    Ciao ...



  • RHBaum schrieb:

    bei den anderen binaeren verkettenbaren operatoren hasst natuerlich vollkommen recht ...

    Sofern es sich um Assignment Operatoren handelt, dann 'foo&', bei Non-Assignment 'const foo'.

    RHBaum schrieb:

    Nur weil es bei den Pods geht, soll es mit meinen Klassen auch gehen

    Wer spricht denn von PODs? Obwohl es bei denen ja das Gleiche ist, wie mit primitiven Typen. Also ein weiteres Argument gegen 'const foo&'.

    RHBaum schrieb:

    obwohls keinen Sinn macht, find ich irgendwie nicht recht nen gutes Argument

    Wieso macht es keinen Sinn? Hast du noch nie Code geschrieben, in dem Folgendes gemacht wurde?

    b = a = 0;
    

    Nein? Dann kannst du dir vielleicht vorstellen, dass es Leute gibt, die sowas schon verwendet haben (mich eingeschlossen). Ändere nicht die Prinzipien einer Sprache, nur damit sie dir besser passt, und du damit andere mit deinem Erfahrungsschatz einschränkst.

    RHBaum schrieb:

    und wenn wer wirklich schreibt (x=y)=Z halt ich das fuer nen Denk oder tippfehler, wo der compiler ruhig maulen sollte ...

    Nö. Der Compiler soll maulen, wenn's syntaktisch nicht korrket ist, und nicht wenn sich jemand vertippt oder 'nen Denkfehler hat. Niemand hat gesagt, dass C++ eine leichte Sprache ist. Und Fallen gibts überall. Ich denke da nur an

    if (a = 0)
    

    Um solchen Sachen vorzubeugen, schreiben ja einige Leute die Konstante auf die linke Seite, also

    if (0 = a)
    

    Aber was machst du, wenn du zwei l-values hast?
    C++ ist nunmal keine idiotensichere Sprache, und das wird sie auch nie werden. Also versuche erst gar nicht, eine daraus zu machen.

    camper schrieb:

    im übrigen ist (x=y)=z ja nur undefiniert, falls x einen trivialen zuweisungsoperator hat

    Wieso das? Irgendwie steh ich im Moment auf'm Schlauch.



  • Wieso macht es keinen Sinn? Hast du noch nie Code geschrieben, in dem Folgendes gemacht wurde?
    C/C++ Code:
    b = a = 0;
    C/C++ Code:

    Natuerlich verwende ich das auch ....
    und das ist selbst mit dem const correct, weil es ja als
    b = (a = 0) aufgeloest wird, und nicht als (b = a) = 0

    den ruckgabewert verwendest du schon, ich natuerlich manchmal auch, aber ich schreib nie darauf rum, das mein ich damit ...

    Ich lass mich aber immer noch belehren:
    nenn mir nen Fall, wo man auf die Rueckgabe einer Zuweisung rumschreibt, und es auch noch Sinn macht.

    Wer spricht denn von PODs? Obwohl es bei denen ja das Gleiche ist, wie mit primitiven Typen.

    Vielleicht lieg ich da im Begriff falsch, aber bei PODs (plain old data) gehoerten bei mir die primitiven typen immer mit rein ... oder lieg ich da falsch ?
    und da es fuer die nicht primitiven, aber doch PODs (struct union pointer) auch gilt, hab ichs halt auch so ausgedrueckt ...

    Ciao ...



  • RHBaum schrieb:

    Natuerlich verwende ich das auch ....
    und das ist selbst mit dem const correct, weil es ja als
    b = (a = 0) aufgeloest wird, und nicht als (b = a) = 0

    Achso, da hab ich dich missverstanden. Dachte, du hättest was gegen 'x = y = z'. Dass '(x = y) = z' wenig Sinn macht, hatten wir ja schon geklärt.

    RHBaum schrieb:

    nenn mir nen Fall, wo man auf die Rueckgabe einer Zuweisung rumschreibt, und es auch noch Sinn macht.

    Was Sinn macht oder nicht, ist natürlich relativ. Ich könnte mir aber Folgendes Szenario vorstellen:

    void mach_irgendwas(foo& a)
    {
        //...
    }
    
    a = lese_irgendwas();
    mach_irgendwas(a);
    

    Ich würde immer zu dieser Schreibweise tendieren, auch weil's einfach übersichtlicher ist. Wer dennoch eine etwas kompaktere Schreibweise bevorzugt, kann dies natürlich machen. Diese Freiheit bietet C++ nunmal.

    mach_irgendwas(a = lese_irgendwas());
    

    Diese Möglichkeit würde mit const foo& als Rückgabe vom op= nicht mehr funktionieren.

    RHBaum schrieb:

    Vielleicht lieg ich da im Begriff falsch, aber bei PODs (plain old data) gehoerten bei mir die primitiven typen immer mit rein ... oder lieg ich da falsch ?

    Laut Wiki nicht. Ich dachte eigentlich immer, dass PODs Strukturen sind, die keine selbstdefinierten Konstruktoren und Operatoren haben. Aber offensichtlich gehören die primitiven Typen auch dazu. Man lernt halt nie aus. 😉 Naja, wie auch immer. Ich sprach allerdings nur von primitiven Typen. Dass Strukturen, die keinen selbstdefinierten op= haben und wiederum nur PODs als Member, sich genauso verhalten, sollte einen ja noch zusätzlich nachdenklich machen, dass man mit 'const foo&' bestehende Regeln bricht.


  • Mod

    groovemaster schrieb:

    camper schrieb:

    im übrigen ist (x=y)=z ja nur undefiniert, falls x einen trivialen zuweisungsoperator hat

    Wieso das? Irgendwie steh ich im Moment auf'm Schlauch.

    das problem ist dorch, dass x zweimal zwischen zwei sequence points verändert wird, richtig?
    ist nun zuweisungsoperator nicht trivial, handelt es sich um ganz normale funktionsaufrufe (nur mit operator schreibweise). alle seiteneffekte durch die initialisierung eines funktionsparameters müssen abgeschlossen sein, bevor die funktion selbst aufgerufen wird.
    beispiel:
    wir haben noch op<<= für streams überladen, so dass es sich wie op << verhält (beide geben ihren linken operanden weiter, so wie op= es tut).
    dann wird niemand erwarten, dass

    (cout<<=a)<<=b;
    

    undefiniert ist. für builtins wäre es das aber.



  • Verstehe trotzdem noch nicht, was daran undefiniert sein soll. Der Compiler macht aus '(x = y) = z' im Grunde doch

    x = y, x = z
    

    Egal, ob es sich um einen trivialen oder nicht-trivialen op= handelt, ist das doch definiert. 😕


  • Mod

    #include <iostream>
    int main()
    {
    int a, b = 1, c = 2;
    std::cout << ( ( a = b ) = c ) << std::endl;
    std::cout << a << std::endl;
    }
    

    in der ersten zeile wird auf jeden fall 2 ausgegeben. was in der 2. zeile steht, ist dagegen unbestimmt. der compiler darf hier machen, was er will.


Anmelden zum Antworten