operator +\- überladen - Warnugen



  • Seitdem ich beim letzten mal als ich mit dem Operator (++) rumgespielt habe so auf die "Schnauze" gefallen bin, (vll. kann sich ja jemand noch an den Thread erinner, war gestern) will ich das hier doch mal von euch checken lassen. Irgendwas stimmt hier sowieso nicht, weil 2 blöde Warnugen kommen. Es stört den Compiler irgendwie das ich in meiner "operator überladen" Funktion eine Referenz zurückgebe. Aber gestern hab ich doch noch gelernt, das ich nicht das Objekt zurückgeben soll, sondern eine Referenz!!! Sonst wird eine Kopie gemacht.....und alles geht zu langsam! Mh :(! Jetzt hab ich irgendwie schon wieder n Problem mit Referenzen, und ich hab gedacht ich hätte des gelöst....ich erinnere mich da bloß an lange Erklärungen im gestrigen Thread von euch :(!!!
    Also hier ma der Code:

    #include <iostream>
    using namespace std;
    
    class math
    {
        public:
        math(int vA);
        ~math();
        math& operator+ (math &rhs);
        math& operator- (math &rhs);
        int Get_A() {return A;}
    
        private:
        int A;
    };
    
    math::math(int vA)
    :A(vA)
    {
    
    }
    
    math::~math()
    {
    
    }
    
    math& math::operator+ (math &rhs)
    {
        math temp(A+rhs.Get_A());
        return temp;
    }
    
    math& math::operator- (math &rhs)
    {
        math temp(A-rhs.Get_A());
        return temp;
    }
    
    int main()
    {
        math TESTING_A(17);
        math TESTING_B(5);
        math TESTING_C(0);
        TESTING_C=TESTING_A-TESTING_B-TESTING_B;
        cout << TESTING_C.Get_A() << endl;
    
        return 0;
    }
    

    Compilermeldung:

    Compiling: C:\MinGW\Andi\pak_roc.cpp
    C:\MinGW\Andi\pak_roc.cpp: In member function math& math::operator+(math&)': C:\\MinGW\\Andi\\pak_roc.cpp:30: warning: reference to local variabletemp' returned
    C:\MinGW\Andi\pak_roc.cpp: In member function math& math::operator-(math&)': C:\\MinGW\\Andi\\pak_roc.cpp:36: warning: reference to local variabletemp' returned
    Linking console executable: C:\MinGW\Andi\pak_roc.exe
    Process terminated with status 0 (0 minutes, 0 seconds)
    0 errors, 2 warnings

    Checking for existence: C:\MinGW\Andi\pak_roc.exe
    Executing: C:\MinGW/cb_console_runner.exe "C:\MinGW\Andi\pak_roc.exe" (in C:\MinGW\Andi)
    Process terminated with status 0 (0 minutes, 2 seconds)



  • Eine Referenz auf lokale Variablen ist halt schlecht, weil die lokalen Variablen aufhören zu existieren wenn du den Scope (also die Funktion verlässt). Hier musst du eine Kopie zurück geben. Aber moderne Compiler optimieren die idr. weg.

    operator+/- sollten kein Member sein. Siehe http://www.gotw.ca/gotw/004.htm

    Außerdem solltest du auf const-correctness achten

    (und großgeschriebene Variablennamen sind so was von hässlich :p)



  • So hier wären ein paar Fragen zu dem Internetlink von dir, ich hab mir gleich alles angeschaut, und es handelt sich jetzt nicht nur noch explizit um den Operator +/-.
    Der Autor hat folgendes geschrieben:
    1.

    "Why write a Complex class when one already exists in the standard library? (And, incidentally, one that doesn't have any of the following problems and has been crafted based on years of practice by the best people in our industry? Humble thyself and reuse!)

    Reuse standard library algorithms instead of handcrafting your own. It's faster, easier, AND safer!

    Wie soll den diese library heißen? Und wenn ich diese dann verwende, dann sind die ganzen Operatoren wie "+","-","=","++"....schon extra für mich überladen, also für so standard Operationen wie A=C+B. Oder was meint der damit?

    2.

    Prefer using "a op= b" instead of "a = a op b" for arithmetic operations (where appropriate; some classes -- not any that you wrote, right? -- might not preserve the natural relationship between their op and op=).

    Also so ungefähr hab ichs verstanden, er sagt doch das in manchane Klassen die Konstelation A=A+B irgendwie nicht funktioniert, und man darum immer A +=B nutzen soll. Hab ich das richtig verstanden? Soll ich jetzt in Zukunft nie mehr A=A+B nutzen sondern immer A +=B? Was genau meint der da jetzt, was soll den bei A=A+B passieren? Kanst du mir das nochmal erklären?

    3.

    operator+ should not be a member function. If it's a member like this, you can write "a=b+1" but not "a=1+b".

    Okay, das verstehe ich. Da kann ich dir und dem Autor nur recht geben. 😃

    4.

    Prefer these guidelines for making an operator a member vs. nonmember function: (Lakos96: 143-144; 591-595; Murray93: 47-49)
    - unary operators are members
    - = () [] and -> must be members
    - += -= /= *= (etc.) are members
    - all other binary operators are nonmembers

    Das Wort "unary" ist gefallen. Zu deutsch glaub "unär", das sind doch Operatoren wie "++", "--" die nicht zwei Seiten haben wie z.B. "+","=" und "-"?
    Oder?
    Was meint er mit dem Punkt hier:
    "- = () [] and -> must be members", z.B. "=" kann ich doch auch außerhalb der Klasse überladen, was hindert mich daran, und was ist daran besser wenn es inder Klasse überladen wird? Genauso bei den anderen Operatoren?
    "- += -= /= *= (etc.) are members" gibts dafür auch eine Erklärung? Was meint er mit "are members"?

    5.

    Style: You should normally define op= if you define op. Here, you should define operator+=, since you defined operator+. In this case, the above function should be operator+= in any case (with a tweak for the proper return value, see below).

    Ähhh, öööhhh der Satz hier, also ich weiß nicht ob ich das richtig verstehe. Das heißt doch irgendwie, wenn ich den Operator "+" überlade, dann soll ich auch den Operator "+=" überladen oder? Hää? Was genau meint der damit?

    6. - 7.
    Das lass ma mal aus, mit dem Operator "<<" und so steig ich sowieso nicht ganz durch. Das würde glaub den Rahmen sprengen, da muss ich mir eher noch mal ein Stream Tutorial anschauen. Wer ein gutes kennt könnte es ja posten. Wär sehr nett. 🙂

    Schluss
    Ganz zum Schluss sagt er was das man nicht Namen mit so Strichen ("-") verwenden soll. Warum nicht, bzw. was genau sagt der da, des hab ich au net ganz gecheckt. Ich mein ich würde die Dinger nie benutzen, weil ich die Hässlich finde. Trotzdem würds mich interessieren was er damit meint.
    Und noch kurz zu meinen Variablen mit Großbuchstaben, also das war ja nur eine Übung der Code von vorhin, und ich würde es natürlich nie wagen meine Variablen mit Großbuchstaben zu versehen. 😃

    Dankeschön schon mal im Voraus.



  • ich gehe mal nicht auf den text von rüdiger ein. das kann er ruhig selber machen. 😉

    fangen wir heute mal falsch rum an. so hätte ich es geschrieben:

    class math
    {
        public:
        math(int vA);
        ~math();
        math operator+ (const math &rhs) const;
        math operator- (const math &rhs) const;
        inline int Get_A() const {return A;}
    
        private:
        int A;
    };
    
    math::math(int vA)
    :A(vA)
    {}
    
    math::~math()
    {}
    
    math math::operator+ (const math &rhs) const
    {
        return A+rhs.Get_A(); //es reicht den konstruktor implizit aufzurufen
    }
    
    math math::operator- (const math &rhs) const
    {
        return math(A-rhs.Get_A()); //man sollte es aber explizit machen. ;)
    }
    

    eins vorne weg: deinen stil habe ich nicht angetastet, obwohl es mich in den fingern gejuckt hat.

    zuerstmal: ich habe deinen quellcode dahingehend verändert, dass konstante teile jetzt auch genau so makiert sind. man übergibt als parameter eigentlich nie normale referenzen, sondern immer nur const &, daher ist es wichtig, dass alle konstanten aufrufe in deiner klasse auch so makiert sind.

    dann habe ich deinen beiden temp obejekte entfernt, da du sie nicht weiter benutzt. das sollte bei einem halbwegs intelligenten compiler keinerlei unterschied machen, sieht nur aus meiner sicht hübscher aus. wobei die variante mit dem expliziten ctor der mit dem impliziten vorzuziehen ist.

    ich habe dann noch deine Get_A() inline gesetzt. das hat zwei gründe: wenn du den code direkt in die klassen-definitions schreibst, solltest du so etwas immer inline setzen. zum anderen bietet es sich hier an, da die funktion sehr kurz ist. inline ist zwar nur ein hinweis für den compiler, aber für den leser deines quellcodes ist es auch sehr nützlich.
    achja: ich mag inline-funktionen nicht, da sie einen horror darstellen, wenn man versucht binärkompatibilität zu erhalten.

    und zu guter letzt die rückgabewerte. die regel wann man "richtigen" objekte und wann man referenzen zurück gibt ist eigentlich sehr einfach.

    wenn deine klasse sich verändert hat, gibst du die klasse als referenzen zurücken. wenn nicht, gibt du normale objekte zurück.

    will heißen: mathematischen operator wie + oder auch logische operatoren wie || ändern die klasse nicht, daher geben sie ein neues objekte zurück.
    operatoren wie ++ oder << aber auch += und co ändern die klasse und geben daher eine referenzen auf das objekt selber zurück.



  • @ghorst
    Danke erst mal für deine Verbesserungen.

    ghorts schrieb:

    eins vorne weg: deinen stil habe ich nicht angetastet, obwohl es mich in den fingern gejuckt hat.

    Sag mir doch bitte was ich daran verbesser könnte. Das ist doch nur positiv für mich!!! Wenn ich auf so Kleinigkeiten (oder sinds Großigkeiten?) (ich glaub des Wort gibts net :D) in Zukunft mehr drauf achte, dann wird sich mein Stil, mein Code und hoffentlich auch die Compiler Fehler verbessern.

    Zu dem Get_A(), das du inline gesetzte hast, also ich hab immer gedacht, Funktionen die man innerhlab der Klasse reinschreibt sind automatisch inline. Dann werde ich das in Zukunft auch mit inline machen!

    Das hier versteh ich aber leider gar nicht:

    return A+rhs.Get_A(); //es reicht den konstruktor implizit aufzurufen
    

    Da wird jetzt doch nur eine Zahl zurückgegeben, oder? Oder meinst du jetzt das der Compiler so intelligent ist und genau die Schritte ausführt dich ich gemacht habe:

    math& math::operator+ (const math &rhs) const
    {
        math temp(A+rhs.Get_A());
        return temp;
    }
    

    Aber ich mein Schätzungsweiße, wenn mein Objekt jetzt noch 2 weiter Elementvariblen hätte, also int B,C;....an wenn sollte der Compiler dann die Zahl schicken?
    Beim - Operator verstehe ich was du machst, das is klar, da machst du eigentlich doch genau das selbe wie ich, nur das der Compiler sozusagen ein unsichtbares Objekt zurückgibt, ich hab meinem Objekt halt den Namen "temp" gegeben.

    math math::operator- (const math &rhs) const
    {
        return math(A-rhs.Get_A()); //man sollte es aber explizit machen. ;)
    }
    

    Meinst du das es zwischen:

    math& math::operator- (const math &rhs) const
    {
        math temp(A-rhs.Get_A());
        return temp;
    }
    

    und

    math math::operator- (const math &rhs) const
    {
        return math(A-rhs.Get_A()); //man sollte es aber explizit machen. ;)
    }
    

    Schnelligkeitsunterschied gibt? Oder gehts hier wirklich nur ums aussehen?

    Also das du beim + operator am Schluss eine Zahl zurückgibst, und beim - Operator das mit "return math(...." machst verstehe ich nicht. Könntes es sein das du beim + Operator das "math" vergessen hast? Weil sonst verstehe ich die Welt schon wieder nicht mehr.

    Dankeschön schon mal im Voraus.

    PS:
    Zum "const" noch kurz, wäre es nicht auch gut wenn man bei beiden Operatoren, am Schluss das "math" Objekt noch const macht? Oder ist das egal?

    const math operator- (const math &rhs) const;
    


  • Stromberg schrieb:

    Das Wort "unary" ist gefallen. Zu deutsch glaub "unär", das sind doch Operatoren wie "++", "--" die nicht zwei Seiten haben wie z.B. "+","=" und "-"?
    Oder?

    Richtig

    Stromberg schrieb:

    Was meint er mit dem Punkt hier:
    "= () [] and -> must be members", z.B. "=" kann ich doch auch außerhalb der Klasse überladen, was hindert mich daran, und was ist daran besser wenn es inder Klasse überladen wird?

    Der Compiler erzeugt Dir automatisch einen "operator=", daher mußt Du in der Klasse diesen Operator überladen falls das notwendig sein sollte. Bei den anderen hast Du meines Wissens gar keine andere Wahl (bin gerade zu faul in der Norm nachzuschauen).

    Stromberg schrieb:

    "+= -= /= *= (etc.) are members" gibts dafür auch eine Erklärung?

    Effizienz (ich spar mir wieder den Blick in die Norm, es könnte auch sein, daß es nur so geht), da mit diesen Operatoren ein Objekt direkt verändert wird, ist es nicht sinnvoll das außerhalb der Klasse zu tun.

    Stromberg schrieb:

    Was meint er mit "are members"?

    Member functions vs. freestanding functions
    Also Implementation als Methode im Gegensatz zu einer freien Funktion.

    Stromberg schrieb:

    Ähhh, öööhhh der Satz hier, also ich weiß nicht ob ich das richtig verstehe. Das heißt doch irgendwie, wenn ich den Operator "+" überlade, dann soll ich auch den Operator "+=" überladen oder?

    Korrekt, das ist so sinnvoll.

    Stromberg schrieb:

    Hää? Was genau meint der damit?

    Was ist Dir daran unklar?

    Stromberg schrieb:

    Ganz zum Schluss sagt er was das man nicht Namen mit so Strichen ("-") verwenden soll.

    Er meint "_" und nicht "-", und das bezieht sich auf folgenden Programmierstil.

    class MyClass {
        int _i; // das kann Probleme geben, da _Bezeichner und __Bezeichner
                // für den Compiler und die Laufzeitumgebung freizuhalten sind
                // Genaueres steht in der Norm (s.o.)
    
        int i_; // dagegen ist problemfrei, was man auch meistens benutzt
        int m_i; // wird dagegen von anderen Personen bevorzugt
    };
    


  • Stromberg schrieb:

    math math::operator- (const math &rhs) const
    {
        return math(A-rhs.Get_A()); //man sollte es aber explizit machen. ;)
    }
    

    Das nennt sich Return-Value-Optimization, der Compiler hat die Wahlfreiheit das temporäre Objekt wegzuoptimieren. Wenn Du das getrennt schreibst, gibt es die Möglichkeit nicht mehr.

    Grüße



  • ~john schrieb:

    Das nennt sich Return-Value-Optimization, der Compiler hat die Wahlfreiheit das temporäre Objekt wegzuoptimieren. Wenn Du das getrennt schreibst, gibt es die Möglichkeit nicht mehr.

    Grundsätzlich richtig, aber spätestens sein NRVO kann ein besserer Compiler auch die getrennte Variante optimieren.



  • Stromberg schrieb:

    Sag mir doch bitte was ich daran verbesser könnte. Das ist doch nur positiv für mich!!! Wenn ich auf so Kleinigkeiten (oder sinds Großigkeiten?) (ich glaub des Wort gibts net :D) in Zukunft mehr drauf achte, dann wird sich mein Stil, mein Code und hoffentlich auch die Compiler Fehler verbessern.

    du könntest konsequent in der bezeichnung sein. 😉
    in der stl werden klassen wie auch variablen grundsätzlich klein geschrieben, wobei die variablen meist ein präfix haben.
    es gibt andere libs, die sich an den java-standard halten und klassen groß und variablen klein schreiben. irgendeine form der unterscheidung sollte man aber verweden, wobei variabeln groß und klassen klein extrem seltsam aussieht.

    Stromberg schrieb:

    Zu dem Get_A(), das du inline gesetzte hast, also ich hab immer gedacht, Funktionen die man innerhlab der Klasse reinschreibt sind automatisch inline.

    sie sind nicht automatisch inline, aber die meisten compiler interpretieren es so.

    Stromberg schrieb:

    Das hier versteh ich aber leider gar nicht:

    return A+rhs.Get_A(); //es reicht den konstruktor implizit aufzurufen
    

    Da wird jetzt doch nur eine Zahl zurückgegeben, oder?

    in c++ gibt es eine implizite umwandlung zwischen objekten, wenn die klasse einen passenden ctor bereit stellt.
    will heißen, wenn du dein ctor der form "klasse(int)" hast, dann kannst du an jeder stelle im code ein int anstelle deiner klasse verwenden. der compiler wird das dann durch den aufruf des ctors ersetzen.
    allerdings sollte man die finger von derartigen konstrukten lassen und möglichst immer den ctor explizit aufrufen.

    Stromberg schrieb:

    Aber ich mein Schätzungsweiße, wenn mein Objekt jetzt noch 2 weiter Elementvariblen hätte, also int B,C;....an wenn sollte der Compiler dann die Zahl schicken?

    wenn du mehr als einen parameter hast oder genauer mehr als einen parameter ohne defaultwert, dann ist diese variante nicht möglich.

    Stromberg schrieb:

    Meinst du das es zwischen:

    math& math::operator- (const math &rhs) const
    {
        math temp(A-rhs.Get_A());
        return temp;
    }
    

    und

    math math::operator- (const math &rhs) const
    {
        return math(A-rhs.Get_A()); //man sollte es aber explizit machen. ;)
    }
    

    Schnelligkeitsunterschied gibt? Oder gehts hier wirklich nur ums aussehen?

    es gibt einen geschwindigkeitsunteschied. die erste variante funktioniert nämlich nicht. 😉
    man kann keine referenz von lokalen objekten zurückgeben.
    wenn die frage dahin ging, ob die variante mit oder ohne lokalen objekte schneller ist, ist die antwort "it depends". es kann im ersten fall sein, dass dein compiler strohdoof ist und tatsächlich erst eine objekt erzeugt und dann bei return mittels copy-ctor ein neues erstellt. bei der anderen variante sollte dieses nie geschehen.

    Stromberg schrieb:

    PS:
    Zum "const" noch kurz, wäre es nicht auch gut wenn man bei beiden Operatoren, am Schluss das "math" Objekt noch const macht? Oder ist das egal?

    const math operator- (const math &rhs) const;
    

    man sollte nie konstante objekte zurückgeben, da das nicht sinn und zweck der übung ist. bsp:

    math a,b;
    return ++(a+b);
    

    der aufruf ist völlig legal und setzt zwingend voraus, dass der rückgabewert von operator+ eine nicht konstantes objekt ist.
    bei dem streaming-operator wird es noch deutlicher:

    a << b << c;
    

    bedeutet eigentlich:
    [cpp]
    (a << b) << c;
    [/cpp}
    hier ist es unbedingt notwendig, dass operator<< keine konstante referenz liefert, da sonst der aufruf für << c nicht mehr möglich wäre.



  • Siehts so besser aus:

    #include <iostream>
    using namespace std;
    
    class Math
    {
        public:
        Math(int v_a);
        ~Math();
        Math operator+ (const Math &rhs) const;
        Math operator- (const Math &rhs) const;
        inline int get_a() const {return a;}
    
        private:
        int a;
    };
    
    Math::Math(int v_a)
    :a(v_a)
    {
    
    }
    
    Math::~Math()
    {
    
    }
    
    Math Math::operator+ (const Math &rhs) const
    {
        return Math(a+rhs.get_a());
    }
    
    Math Math::operator- (const Math &rhs) const
    {
        return Math (a-rhs.get_a());
    
    }
    
    int main()
    {
    
        Math testing_A(17);
        Math testing_B(5);
        Math testing_C(0);
        testing_C=testing_A-testing_B-testing_B-testing_B;
        cout << testing_C.get_a() << endl;
    
        return 0;
    }
    


  • so kann man das lassen. 😉

    nein spaß bei seite: es passt so, wenn es zu den coding-richtlinienen deines brötchengebers passt bzw. für dein heimprojekt, wenn du es konsequent durchhältst.
    ich persönlich bevorzuge es member-variablen mit m_var zu kenntzeichen. auch bin ich ein fan von d-pointern. aber beides spielt hier keine rolle, wenn du konsequent deinen stil verfolgst.



  • Stromberg schrieb:

    "Why write a Complex class when one already exists in the standard library? (And, incidentally, one that doesn't have any of the following problems and has been crafted based on years of practice by the best people in our industry? Humble thyself and reuse!)

    Reuse standard library algorithms instead of handcrafting your own. It's faster, easier, AND safer!

    Wie soll den diese library heißen? Und wenn ich diese dann verwende, dann sind die ganzen Operatoren wie "+","-","=","++"....schon extra für mich überladen, also für so standard Operationen wie A=C+B. Oder was meint der damit?

    Für komplexe Zahlen gibt es die (Template)Klasse std::complex<> (und die Bemerkung bezog sich auf das dort gegebene Beispiel ;))

    2.

    Prefer using "a op= b" instead of "a = a op b" for arithmetic operations (where appropriate; some classes -- not any that you wrote, right? -- might not preserve the natural relationship between their op and op=).

    Also so ungefähr hab ichs verstanden, er sagt doch das in manchane Klassen die Konstelation A=A+B irgendwie nicht funktioniert, und man darum immer A +=B nutzen soll. Hab ich das richtig verstanden? Soll ich jetzt in Zukunft nie mehr A=A+B nutzen sondern immer A +=B? Was genau meint der da jetzt, was soll den bei A=A+B passieren? Kanst du mir das nochmal erklären?

    Bei "gut" geschriebenen Klassen liefert "A+=B;" und "A=A+B;" das gleiche Verhalten - aber ersteres ist häufig schneller, weil es keine temporären Hilfsvariablen anlegen muß. Und für Konsistenz wird oft op+ durch op+= ausgedrückt (das könntest du für deine Klasse auch verwenden):

    class math
    {
      int m_a;
    public:
      math(int a) : m_a(a) {}
    
      math& operator+=(const math& other)
      { m_a+=other.m_a; return *this; }
      ...
    };
    
    math operator+(const math&l,const math&r)
    {
      return math(l)+=r;
    /* alternativ:
      math tmp(l);
      return tmp+=r;
    */
    }
    

    3.

    operator+ should not be a member function. If it's a member like this, you can write "a=b+1" but not "a=1+b".

    Okay, das verstehe ich. Da kann ich dir und dem Autor nur recht geben. 😃

    4.

    Prefer these guidelines for making an operator a member vs. nonmember function: (Lakos96: 143-144; 591-595; Murray93: 47-49)
    - unary operators are members
    - = () [] and -> must be members
    - += -= /= *= (etc.) are members
    - all other binary operators are nonmembers

    Das Wort "unary" ist gefallen. Zu deutsch glaub "unär", das sind doch Operatoren wie "++", "--" die nicht zwei Seiten haben wie z.B. "+","=" und "-"?
    Oder?

    Ja, unäre Operatoren sind alle, die nur einen Operanden haben - unter anderem Inkrement oder Vorzeichen. (im weitesten Sinne zählen auch Postfix-Inkrement und -Dekrement als unär, aber die werden mitunter gesondert betrachtet)

    Was meint er mit dem Punkt hier:
    "- = () [] and -> must be members", z.B. "=" kann ich doch auch außerhalb der Klasse überladen, was hindert mich daran, und was ist daran besser wenn es inder Klasse überladen wird? Genauso bei den anderen Operatoren?

    Nein, die Zuweisung kannst/darfst du nicht global überladen.

    "- += -= /= *= (etc.) are members" gibts dafür auch eine Erklärung? Was meint er mit "are members"?

    Kurzfassung: Bei denen macht's keinen Sinn, Möglichkeiten wie "7+=b;" anzubieten (und außerdem liegen sie noch nahe an der reinen Zuweisung). Und das "are members" kannst du in dem Zusammenhang lesen als "sollten Methoden sein".

    5.

    Style: You should normally define op= if you define op. Here, you should define operator+=, since you defined operator+. In this case, the above function should be operator+= in any case (with a tweak for the proper return value, see below).

    Ähhh, öööhhh der Satz hier, also ich weiß nicht ob ich das richtig verstehe. Das heißt doch irgendwie, wenn ich den Operator "+" überlade, dann soll ich auch den Operator "+=" überladen oder? Hää? Was genau meint der damit?

    Genau das meint er damit - siehe auch die Erklärungen zu Punkt 2.



  • Äh noch kurz was, is mir grad durch Zufall aufgefallen:

    ghorst schrieb:

    man sollte nie konstante objekte zurückgeben, da das nicht sinn und zweck der übung ist. bsp:

    C/C++ Code:
    math a,b;
    return ++(a+b);
    C/C++ Code:
    math a,b;
    return ++(a+b);

    der aufruf ist völlig legal und setzt zwingend voraus, dass der rückgabewert von operator+ eine nicht konstantes objekt ist.
    bei dem streaming-operator wird es noch deutlicher:

    C/C++ Code:
    a << b << c;
    C/C++ Code:
    a << b << c;

    Der Typ von der Englischen Seite hat aber am Schluss folgenden Code:

    const Complex operator+( const Complex& lhs, const Complex& rhs ) 
    {
            Complex ret( lhs );
            ret += rhs;
            return ret;
    }
    

    ist das jetzt richtig oder is es n Fehler?

    Dankeschön schon mal im Voraus.
    Stromberg


  • Mod

    Stromberg schrieb:

    Der Typ von der Englischen Seite hat aber am Schluss folgenden Code:

    const Complex operator+( const Complex& lhs, const Complex& rhs ) 
    {
            Complex ret( lhs );
            ret += rhs;
            return ret;
    }
    

    ist das jetzt richtig oder is es n Fehler?

    Es ist nicht falsch. Anderseits ist bei diesen Dingen nie das letzte Wort gesprochen. Immerhin ist der betreffende Artikel schon verhältnismäßig alt.
    Worauf zum Beispiel gründet sich die Empfehlung des Autors, die kombinierten Operatoren (also += -= usw.) als Member zu implementieren? Die Sprache erfordert das nicht - und auch der Vergleich mit primitiven Typen wie int, double spricht eher dagegen. Der Fakt, dass diese Operatoren das Objekt verändern, kann es allein nicht sein - die Streamoperatoren tun das auch (und dort bleibt uns sowieso nichts anderes übrig, als eigene Operatore frei zu implementieren. Erinnern wir uns auch an die Analyse des gleichen Autors hinsichtlich std::string - dort richtet sich die Wahl zwischen freier Funktion und Memberfunktion keineswegs (allein) danach, ob die betreffende Funktion den String verändert. Es geht dort eher um die Unterscheidung zwischen minimalen und "bequemen" Interface. Operatoren, die isoliert implementiert werden, haben nun die unangenehme Eigenschaft, zwei Herren zu dienen. Einerseits erlauben sie die Ausführung einer bestimmten Operation mit dem Objekt (das könnten wir aber auch mit einer normalen Funktion haben). Andererseits präsentieren sie auch noch ein bequemes Interface für den Aufruf. Wohlgemerkt: betrachte diesen Beitrag nicht als Opposition zum verlinkten Artikel, sondern nur als Anstoß um weiterzudenken.



  • Vll. hab ich jetzt mal wieder nen Denkfehler - weil schon so spät ist 😃 - aber warum wird beim Operator "+=" eigentlich noch das "*this" zurückgegeben? Es reicht doch wenn die jeweilige Elemntavriable bearbeitet worden ist....
    Kann mir jemand mal n Beispiel sagen, in dem das "*this" wichtig ist?

    Op & Op::operator+= (const Op &rhs)
    {
        m_a += rhs.get_a();
        return *this;
    }
    

    Dankeschön schon mal im Voraus.



  • p=allocate_more_memory(current_mem_size+=step_size);
    

    kommt selten vor - aber es spricht nichts dagegen soetwas zu verbieten, oder?



  • Stromberg schrieb:

    Der Typ von der Englischen Seite hat aber am Schluss folgenden Code:

    const Complex operator+( const Complex& lhs, const Complex& rhs ) 
    {
            Complex ret( lhs );
            ret += rhs;
            return ret;
    }
    

    ist das jetzt richtig oder is es n Fehler?

    ein fehler ist es nicht. es ist aber so, dass sich die klasse nicht so verhalten wird, wie es der nutzer erwartet. so funktioniert bspw das hier nicht so wie erwartet:

    Complex a,b;
    return -(a+b);
    

    ich bin mir allerdings gerade nicht ganz sicher, was da passiert. entweder es kompiliert gar nicht oder es kann auch sein, dass hier der compiler implizit ein neues Complex-objekt erstellt. aber beides ist nicht das, was der nutzer des codes erwartet.
    der erwartet wohl am ehsten, dass da ein objekt als rückgabe wert von + erzeugt wird und dieses dann mit dem unären - operator verändert wird und zu guter letzt zurückgegeben wird (wobei hier wieder eine copy-ctor fällig wird).

    aber warum wird beim Operator "+=" eigentlich noch das "*this" zurückgegeben? Es reicht doch wenn die jeweilige Elemntavriable bearbeitet worden ist....

    es würde in den meisten fällen reichen, allerdings widerspricht es dann dem verhalten der "normalen" operatoren. ein bsp hat shade of mine ja bereits gezeigt. ich denke es wird deutlicher, wenn man das ganze am operator= betrachtet.

    if (a=function())
     [..]
    

    das kommt relativ häufig vor, etwa beim öffnen von datein und ähnlichem. hier wird der rückgabewert des operator= genutzt, um die if-abfrage sicherzustellen. spart also eine zeile code.


  • Mod

    ghorst schrieb:

    der erwartet wohl am ehsten, dass da ein objekt als rückgabe wert von + erzeugt wird und dieses dann mit dem unären - operator verändert wird und zu guter letzt zurückgegeben wird (wobei hier wieder eine copy-ctor fällig wird).

    Ich glaube, du bist der Einzige, der erwartet, dass dieser unäre Operator sein Argument verändert. (Diagnose: Kopierphobie 😉 )



  • Okay danke @ all.
    Ich würde sagen das sind jetzt alles eher n bissel Komplexe Fälle...und so öfter ich drüber nachdenke, desto mehr veriwrr ich mich dann immer. Aber mir is auch klar warum man jetzt bei "+=" *this zurück gibt... Also so im allgemeinen bin ich mit Operatoren jetzt glaub ganz fit, und so komplizierte Fälle werden ja nicht so oft dran kommen, und wenn dann meld ich mich wieder bei euch ^^ 😃 .

    MfG
    Stromberg


Anmelden zum Antworten