Binärer Operator hat zu viele Parameter



  • Hi Leute,

    stecke leider schon wieder in Schwierigkeiten. Hoffe, ihr könnts mir dabei helfen. Danke!
    Ich möchte statt die friends mit der Funktion getz und getn arbeiten, also den Zugriff auf die Klasse anders gewährleisten, aber bekomme die Fehlermeldung wegen binären Operator. Habe ich etwas falsch gemacht?

    #include <iostream>
    using namespace std;
    
    // Prototyp
    class Bruch {
    
    	//Attribute
    	private: // standardmässig auch auf privat gesetzt. 
    		int z; // eigenschaften (Daten) der Klasse
    		int n; // eigenschaften
    
    	public: // zugriffe von außen möglich, nur wenn man public schreibt. z.b. bei objekten oder alles außerhalb der klasse
    		// Konstruktordeklaration  - weil Klasse Bruch und der KOnstruktor auch Bruch heisst. Außerdem geben Konstruktoren keine Rückgabewerte.	
    		Bruch (int Zaehler=0, int Nenner=1); 
    		int getZ() const;
    		int getN() const;
    
    		Bruch operator+(const Bruch& lhs, const Bruch& rhs);
     // Operatoren für die Rechnungen
    		Bruch operator-(const Bruch& lhs, const Bruch& rhs); // es darf ein & stehen, aber ist kein muss
    // Operatoren für die Rechnungen
    		Bruch operator*(const Bruch& lhs, const Bruch& rhs); // es darf ein & stehen, aber ist kein muss
    // Operatoren für die Rechnungen
    		Bruch operator/(const Bruch& lhs, const Bruch& rhs); // es darf ein & stehen, aber ist kein muss
    
     // Ist für die Ausgabe zuständig
    		ostream& operator <<(ostream& os, const Bruch& rhs); // & = muss ein und sein
    // Ist für die Eingabe zuständig
    		istream& operator >>(istream& is, const Bruch& rhs);
    
    };
    
    inline // ruft das unterprogramm auf und liest den lhs.z 
    int Bruch::getZ() const
    { // getZ = name des unterprogramms. Bruch= es gehört zu bruch und ist konstant.
    	return z;
    }
    
    inline // ruft das unterprogramm auf und liest den lhs.z 
    int Bruch::getN() const
    { // getZ = name des unterprogramms. Bruch= es gehört zu bruch und ist konstant.
    	return n;
    }
    
    /* was soll der code tun – das echte Programm - */
    //Konstruktordefinition  --> Klassenname::Klassenname(Parameter)
    Bruch:: Bruch(int Zaehler, int Nenner):z(Zaehler), n(Nenner)
    { // -> = Pfeiloperator
    this -> z = Zaehler;  /* von aktuellen obj. der Zaehler wird auf diesen wert gesetzt. Man kann this verwenden, ist nur eine längere schreibweise. */
    n = Nenner;
    }
    
    Bruch operator+(const Bruch& lhs, const Bruch& rhs)
    {	
    	int Nenner = lhs.getN() * rhs.getN();
    	int Zaehler = lhs.getZ() * rhs.getN() + rhs.getZ() * lhs.getN();
    	return Bruch(Zaehler, Nenner); // Bastle Bruch und dann return.
    }
    
    Bruch operator-(const Bruch& lhs, const Bruch& rhs)
    {
    	int Nenner = lhs.getN() * rhs.getN();
    	int Zaehler = lhs.getZ() * rhs.getN() - rhs.getZ() * lhs.getN();
    	return Bruch(Zaehler, Nenner); // Bastle Bruch und dann return.
    }
    
    Bruch operator*(const Bruch& lhs, const Bruch& rhs)
    {
    	int Nenner = lhs.getN() * rhs.getN();
    	int Zaehler = lhs.getZ() * rhs.getZ();
    	return Bruch(Zaehler, Nenner); // Bastle Bruch und dann return.
    }
    
    Bruch operator/(const Bruch& lhs, const Bruch& rhs)
    {
    	int Nenner = lhs.getN() * rhs.getZ();
    	int Zaehler = lhs.getZ() * rhs.getN();
    	return Bruch(Zaehler, Nenner); // Bastle Bruch und dann return.
    }
    
    ostream& operator<<(ostream& os, const Bruch& rhs) // os = cout und Bruch& rhs = Parameter 
    {
    	os << rhs.getZ() << '/' << rhs.getN();
    	return os;
    }
    
    istream& operator>>(istream& is, const Bruch& rhs)
    {
    	char dummy;
    	is >> rhs.getZ() >> dummy >> rhs.getN();
    	return is; 
    }
    
    void main()
    {
    
    Bruch a(1,2); //objekt a
    Bruch b(1,3);
    Bruch c = a + b;
    
    cout << a  << "+" << b << " = " << c <<endl;
    c = a - b;
    cout << a  << "-" << b << " = " << c <<endl;
    c = a * b;
    cout << a  << "*" << b << " = " << c <<endl;
    c = a / b;
    cout << a  << "/" << b << " = " << c <<endl;
    
    // KEttenbruchanfang
    Bruch n(1); // geht auch Bruch n(1,1); -- Variablenwertvergabe
    Bruch w(1); // geht auch Bruch n(1,1); -- Variablenwertvergabe
    int durchlauf = 0;
    do{
    	durchlauf++;
    	//cout << w << endl << endl;
    	//cout << n << endl << endl << "help" <<endl << endl;    
    	w=n/(n+w);
    	//cout << w << endl << endl;
    	//cout << n << endl << endl;
    }while(durchlauf < 4);
    cout << "Kettenbruch (1 + 1 / (1 + 1/(....))) = " << w << endl << endl;
    system("pause");
    
    }
    

    gruß,
    composer



  • Du hast da einen Freund vergessen.



  • Ich will das ohne ein friend lösen. 🙂
    Weil ich schon vorher mit friends gemacht habe und es tadellos funktioniert hat. Meine Aufgabe ist es die Methoden getN() und getZ() zu ergänzen und zwar ohne friends.



  • Ich finde weiterhin, dass du operator+ ein bischen diskriminierst. Alle anderen Operatoren dürfen Freunde von dem Bruch sein, nur der nicht…



  • Ne, also die anderen werde ich auch umschreiben.
    Ich wollte zuerst nur den ersten testen und dann die anderen umschreiben 😃



  • Ohne friend zu arbeiten ist relativ einfach (und imho in so einem Fall auch sinnvoll). Die Funktionen getN und getZ brauchst du dafür nicht.

    class Bruch
    {
    public:
        //...
        Bruch& operator += (const Bruch& rhs);
        //...
    };
    
    Bruch operator + (const Bruch& lhs, const Bruch& rhs)
    {
        Bruch temp = lhs;
        temp += rhs;
        return temp;
    }
    

    Der Vorteil ist, dass die Implementation der Klasse für den globalen Operator vollkommen egal ist. Er benutzt eine Memberfunktion, die du eh hättest schreiben müssen, wenn deine Klasse wie ein Integer benutzt werden soll.

    Gruß
    Don06



  • Es mag schon sein, dass es vorteilhaft wäre, aber leider muss ich getN und getZ benutzen, weil der Prof. uns die Aufgabe gegeben hat.
    Er hat gemeint, wir dürfen den Code nur mit getZ und getN schreiben, anders nicht 😞
    Habe die erste Datei verändert und bekomme lauter obengenannte Fehlermeldungen und dann auch noch diese Fehlermeldung dazu:

    error C2440: 'return': 'const std::istream' kann nicht in 'std::istream &' konvertiert werden. Durch die Konvertierung gehen Qualifizierer verloren.



  • Operator << und >> verändern die Streams. Du solltest sie also ohne const übergeben. Pack die Operatoren in den globalen namespace, aus der Klasse raus, das sollte helfen.



  • Hat mir leider nicht geholfen. Bekomme noch mehr Fehlermeldungen als zuvor.
    Außerdem habe ich ja kein friend mehr stehen, dann müsste ich doch const benutzen oder ?

    Hier die Fehlermeldungen:

    riends\bruch oop.cpp(19) : error C2804: Binärer Operator '+=' hat zu viele Parameter
    riends\bruch oop.cpp(21) : error C2804: Binärer Operator '-' hat zu viele Parameter
    riends\bruch oop.cpp(23) : error C2804: Binärer Operator '*' hat zu viele Parameter
    riends\bruch oop.cpp(25) : error C2804: Binärer Operator '/' hat zu viele Parameter
    riends\bruch oop.cpp(29) : error C2804: Binärer Operator '<<' hat zu viele Parameter
    riends\bruch oop.cpp(31) : error C2804: Binärer Operator '>>' hat zu viele Parameter
    riends\bruch oop.cpp(96) : error C2679: Binärer Operator '>>': Es konnte kein Operator gefunden werden, der einen rechtsseitigen Operanden vom Typ 'int' akzeptiert (oder keine geeignete Konvertierung möglich)
    c:\programme\microsoft visual studio 8\vc\include\istream(1137): kann 'std::basic_istream<_Elem,_Traits> &std::operator >><std::char_traits<char>>(std::basic_istream<_Elem,_Traits> &,signed char *)' sein

    Unten gehts noch weiter mit denen.

    lg



  • Du hattest Recht! Der Fehler lag wirklich an const, hatte blöderweise auch vor dem Bruch das const entfernt, deswegen bekam ich die Fehlermeldungen.

    Die bekomme ich leider noch immer:

    riends\bruch oop.cpp(19) : error C2804: Binärer Operator '+=' hat zu viele Parameter
    riends\bruch oop.cpp(21) : error C2804: Binärer Operator '-' hat zu viele Parameter
    riends\bruch oop.cpp(23) : error C2804: Binärer Operator '*' hat zu viele Parameter
    riends\bruch oop.cpp(25) : error C2804: Binärer Operator '/' hat zu viele Parameter
    riends\bruch oop.cpp(29) : error C2804: Binärer Operator '<<' hat zu viele Parameter
    riends\bruch oop.cpp(31) : error C2804: Binärer Operator '>>' hat zu viele Parameter



  • operator += ist ein Zuweisungsoperator, der darf nur innerhalb der Klasse deklariert werden und hat dann nur _einen_ Parameter, der zweite wird versteckt als this-Zeiger übergeben. Alle anderen operatoren sollten keine Probleme machen, ausser du hast sie nicht aus der Klasse _raus_ genommen.

    class Bruch
    {
    };
    
    operator / //...
    


  • Dankeschön, du bist einfach spitze!
    Habe nur die 6 Operatoren aus der Klasse herausgenommen und sehe das Programm funktioniert perfekt.
    Wünsche euch allen einen schönen Abend!!

    lg


  • Mod

    Don06 schrieb:

    operator += ist ein Zuweisungsoperator, der darf nur innerhalb der Klasse deklariert werden und hat dann nur _einen_ Parameter, der zweite wird versteckt als this-Zeiger übergeben. Alle anderen operatoren sollten keine Probleme machen, ausser du hast sie nicht aus der Klasse _raus_ genommen.

    class Bruch
    {
    };
    
    operator / //...
    

    Die einzigen überladbaren Operatoren, die nur als Memberfunktion überladen werden können, sind: = () [] und ->

    Alle anderen überladbaren Operatoren dürfen als Memberfunktion oder als freie Funktion überladen werden, und dafür kann es gute Gründe geben.
    Der (normalerweise gewünschte) Zusammenhang zwischen += und + und anderen Paarungen lässt es sinnvoll erscheinen, eine Funktion durch die andere auszudrücken. Für gewöhnlich wählt man hier += als Basis und implementiert + durch sie, oft mit der zusätzlichen Behauptung, das wäre effizienter als anders herum).
    Dieses Argument ist aber durchaus nicht so überzeugend, wie es auf den ersten Blick erscheinen mag. Zunächst einmal besteht kein Grund zu der Annahme, die eigentliche Aufgabe der Funktion (hier etwa Addition zweier Argumente), sei nicht ebenso gut durch eine normale Funktion (ob nun frei oder Member sei dahingestellt) durchführbar. Tatsächlich erfüllt eine Funktion += wie oben angeregt eine doppelte Funktion: einerseits addiert sie ihre Argumente, andererseits stellt sie ein bequemes Interface (in Form der Operatorüberladung) zum Zugriff auf diese Addition zur Verfügung. Die Erfahrung sagt uns allerdings, dass jede Funktion/Klasse/etc. für genau eine Aufgabe zuständig sein sollte und zudem Funktionen des öffentlichen Interfaces im Allgemeinen (sans triviale Fälle) nicht die eigentliche Arbeit durchführen sollten. Beide Prinzipien werden hier verletzt.
    Die Annahme, dass es effizient ist, einfache Operatoren durch die entsprechenden Zuweisungsoperatoren zu implementieren, ist zwar in Einzelfällen, keineswegs aber universell wahr. Für Zahlen fällt das zum Beispiel bei der Multiplikation sofort auf. Für große Zahlen wird man hier nicht auf einen Zwischenspeicher verzichten können.

    Um das etwas Zusammenzufassen:
    1. Es ist richtig und eine gute Idee, die eigentliche Arbeit in eine andere Funktion auszulagern - diese sollte aber nicht Teil des öffentlichen Interfaces sein.
    2. Operator+= als Memberfunktion zu implementieren, weil die eigentliche Arbeit Zugriff auf private Teile benötigt, ist kein überzeugendes Argument, denn die eigentliche Arbeit muss dieser Operator gar nicht unbedingt tun.
    3. Operator+= als Memberfunktion hat zur Folge, dass das linke Argument, ein rvalue sein darf, und dieser Operator damit potentiell temporäre Objekte verändert. Für eingebaute Typen ist das nicht möglich. Eine offensichtliche Lösung besteht daher darin, den Operator als freie Funktion (mit Referenz auf nicht-const als linken Paramter) zu implementieren. Wenn man nicht soweit denkt, kommt man auf die (imo dumme) Idee, einfach überall konstante Objekte zurückzugeben - das ist auch eine Form des Durchbreches von Kapselung.
    Persönlich kann ich nichts negatives an der Möglichkeit finden, mit temporären Objekten zu arbeiten. Das Prinzip der geringsten Überraschung widerspricht dem nicht: das besagt nur, dass wenn eine Funktion ein bestimmtes Verhalten mit einem Typen hat, so sollte sie dieses Verhalten auch mit anderen neuen Typen zeigen. += mit integer-rvalues ist aber gar nicht möglich, ein Schluss auf das Verhalten oder Nichtverhalten mit anderen Typen an dieser Stelle daher unsinnig (skalare Typen haben auch keine Memberfunktionen - soll das heißen, dass ich mir beim Bruch nicht Zähler und Nenner per Funktion liefern lassen darf???). Tatsächlich ist das Verhalten von += bei Anwendung auf rvalues ja sofort durch Analogie zu erschließen - dass diese Operation möglich ist, mag überraschend sein (aber es gibt keinen Grund, dagegen etwas zu unternehmen), was diese Operation dann tut, wird dagegen nicht überraschen, und nur darauf sollte es ankommen.
    Nebenbei: friend ist nicht per se böse. Es ist absolut nichts gegen friend bei Funktionen einzuwenden, die Teil des Interfaces sind - denn hier ist die Koppelung zwischen Funktion und Klasse bereits gegeben. Böse ist friend dort, wo unzusammanhängende Funktionen und Klassen gekoppelt werden.

    Nach dem vielen Text noch eine Skizze zur Illustration:

    //Beispielvariante 1:
    class Bruch
    {
    public:
        friend Bruch& operator+=(Bruch&, const Bruch&);
        friend Bruch operator+(const Bruch&, const Bruch&);
        void swap(Bruch&); // throw()
    private:
        Bruch& addiere(const Bruch&);
    //...
    };
    Bruch& operator+=(Bruch& lhs, const Bruch& rhs)
    {
        return lhs.addiere(rhs);
    }
    Bruch operator+(const Bruch& lhs, const Bruch& rhs)
    {
        Bruch tmp( lhs );
        tmp += rhs;
        return tmp;
    }
    
    //Beispielvariante 2:
    class Bruch
    {
    public:
        friend Bruch& operator+=(Bruch&, const Bruch&);
        friend Bruch operator+(const Bruch&, const Bruch&);
        void swap(Bruch&); // throw()
    private:
        struct add_tag {};
        Bruch(const Bruch&, const bruch&, add_tag);
    //...
    };
    Bruch operator+(const Bruch& lhs, const Bruch& rhs)
    {
        return Bruch(lhs,rhs,Bruch::add_tag());
    }
    Bruch& operator+=(Bruch& lhs, const Bruch& rhs)
    {
        (lhs+rhs).swap(lhs);
        return lhs;
    }
    

    Beide Varianten (und es gibt nat. noch Weitere) könnten in bestimmten Fällen effizienter als die andere sein, es besteht aber kein Unterschied im öffentlichen Interface. Natürlich kann das in Einzelfällen - wie immer - auch Overkill sein. Das kann man aber erst feststellen, nachdem man diese Möglichkeiten erwogen hat.


Log in to reply