Klasse Complex (Herb Sutter) Design



  • aus http://www.gotw.ca/gotw/004.htm (im Buch Lektion 20)

    /*Here's a corrected version of the program, ignoring design and style issues not explicitly noted above:*/
    
    class Complex 
    {
    public:
      explicit Complex( double real, double imaginary = 0 )
        : real_(real), imaginary_(imaginary)
      {
      }
    
      Complex& operator+=( const Complex& other )
      {
        real_ += other.real_;
        imaginary_ += other.imaginary_;
        return *this;
      }
    
      Complex& operator++()
      {
        ++real_;
        return *this;
      }
    
      const Complex operator++( int )
      {
        Complex temp( *this );
        ++*this;
        return temp;
      }
    
      ostream& Print( ostream& os ) const
      {
        return os << "(" << real_ << "," << imaginary_ << ")";
      }
    
      private:
        double real_, imaginary_;
      };
    
      const Complex operator+( const Complex& lhs, const Complex& rhs )
      {
        Complex ret( lhs );
        ret += rhs;
        return ret;
      }
    
      ostream& operator<<( ostream& os, const Complex& c )
      {
        return c.Print(os);
      }
    

    Abgesehen von den fehlenden (überladenen und sonstigen) Funktionen, was würdet ihr bezüglich Design und Stil noch ändern?



  • Beispiel für fehlende Funktionen: Damit z.B. double zusammen mit Complex verarbeitet werden können, könnte man wie folgt ergänzen:

    #include <iostream>
    #include <conio.h>
    using namespace std;
    
    class Complex
    {
    public:
    //...
        Complex& operator+= ( double other )
        {
            real_ += other;
            return *this;
        }
    
    //...
    };
    
    //...
    
    const Complex operator+( const Complex& lhs, double rhs )
    {
        Complex ret = lhs;
        ret += rhs;
        return ret;
    }
    
    const Complex operator+( double lhs, const Complex& rhs )
    {
        Complex ret = rhs;
        ret += lhs;
        return ret;
    }
    
    //...
    
    int main()
    {
        double  d = 2.0;
        cout << d << endl;
        Complex a(2.0,1.0),b(1.0,-3.5);
        Complex e = d + a + b + d;
        cout << e << endl;
        Complex c = a + b;
        cout << a << " + " << b << " = " << c;
        getch();
    }
    

    Hat jemand diese Klasse weiter ergänzt und verfeinert? (außer natürlich im Standard. 😉 )



  • Evtl. erwägen, die Klasse als Template zu definieren, damit es nicht zwingend double sein muss.
    Einen impliziten Konstruktor ergänzen, der nur ein Real frisst. Dann kann man z.B. ein double und ein Complex ganz leicht miteinander verrechnen.



  • Warum hatte Herb Sutter ursprünglich die operator<< - Funktion als friend angegeben und dieses im Buch später weg gelassen?

    private:
    http://www.gotw.ca/gotw/004.htm
            double real_, imaginary_;
            friend ostream& operator<<( ostream& os, const Complex& c );
    
    /* im Buch: */
    private:
        double real_, imaginary_;
    


  • Was Sutter macht, weiss ich nicht. Aber friend sollte man nur benutzen, wenn es notwendig ist. Wenn man mit der public-Schnittstelle etwas implementieren kann, gibt es keinen Grund, sich unnötige Freunde zu machen. (Jaja, die Informatiker und ihre soziale Kompetenzen... 🙂 )



  • Erhard Henkes schrieb:

    Warum hatte Herb Sutter ursprünglich die operator<< - Funktion als friend angegeben und dieses im Buch später weg gelassen?

    Weil man friends gerne vermeidet.
    Er hat die gängige Variante mit 'print ist member und op<< ruft print auf' gewählt, um sich so elegant vor dem friend zu drücken 😉



  • Wenig überzeugend... Wenn schon extra eine public-Schnittstelle gebastelt wird, dann sollte es IMHO doch lieber gleich getReal()/getImag() sein. Diese Funktionen können dann auch aus dem operator<< aufgerufen werden. Dann würde die krampfhafte Vermeidung von friend das Klasseninterface um sinnvolle Funktionen bereichern 😋

    http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.9
    http://www.parashift.com/c++-faq-lite/friends.html
    http://www.cuj.com/documents/s=8042/cuj0002meyers/



  • in http://www.gotw.ca/gotw/004.htm benutzt er aber print als member und op<< außerhalb der Klasse und verwendet dennoch friend. 😕



  • Sollte Print() wirklich ein protected member sein, sodass friend für op<< notwendig wird? 😕 Kann Herb Sutter wirklich irren? 😋

    //...
    protected:    
        ostream& Print( ostream& os ) const
        {
            return os << "(" << real_ << "," << imaginary_ << ")";
        }    
    
    private:
        double real_, imaginary_;
        friend ostream& operator<<( ostream& os, const Complex& c );
    };
    

    Wie ist es wirklich richtig und warum?

    Die Regeln von Scott Meyers in http://www.cuj.com/documents/s=8042/cuj0002meyers/

    if (f needs to be virtual)
       make f a member function of C;
    else if (f is operator>> or
             operator<<)
       {
       make f a non-member function;
       if (f needs access to non-public
           members of C)
          make f a friend of C;
       }
    else if (f needs type conversions
             on its left-most argument)
       {
       make f a non-member function;
       if (f needs access to non-public
           members of C)
          make f a friend of C;
       }
    else if (f can be implemented via C's
             public interface)
       make f a non-member function;
    else
       make f a member function of C;
    

    werden von beiden Varianten befolgt. Mittels protected ist die Kapselung allerdings besser. Warum hat Herb Sutter sich im Buch gegen friend entschieden?



  • Was Sutter macht, weiss ich nicht. Aber friend sollte man nur benutzen, wenn es notwendig ist. Wenn man mit der public-Schnittstelle etwas implementieren kann, gibt es keinen Grund, sich unnötige Freunde zu machen.

    Weil man friends gerne vermeidet.

    Dieses Argument wird in http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.9 allerdings entkräftet. friends unterstützen demnach die erwünschte Kapselung, weil man private oder protected anstelle public für member-functions verwenden kann, anstatt diese zu untergraben.

    Demnach folgere ich, dass Herb Sutter hier evtl. nicht die richtige Entscheidung getroffen hat. protected für Print und friend für op<< wären in diesem Sinne konsequenter gewesen. Oder doch nicht? 🙄



  • Ich werde zur besseren Übersicht für die Frage friend oder non-friend einen neuen Thread öffnen, da es hier um ein eigentlich neues Thema geht.


Anmelden zum Antworten