Überladen von operator<<



  • manni66 schrieb:

    Da du nur Schnipsel zeigst, muss ich raten: es fehlt #include<iostream> im Header.

    ich wollte es nur übersichtlich halten daher habe ich nur die betroffenen Stellen kopiert, aber hier der komplette Code (Bruch.h und Bruch.cpp)

    Bruch.h

    #pragma once
    
    class Bruch
    {
    
    public:
    	Bruch(int, int);
    
    	void	print();
    	void	mul(Bruch&);
    	int		getZaehler();
    	int		getNenner();
    
    	Bruch operator*(const Bruch& b);
    	friend ostream& operator<<(ostream&, const Bruch&);
    
    private:
    	int zaehler, nenner;
    };
    

    Bruch.cpp

    #include "Bruch.h"
    #include <iostream>
    
    using namespace std;
    
    Bruch::Bruch(int z, int n)
    {
    	zaehler = z;
    	nenner = n;
    }
    
    void Bruch::print() {
    	cout << "(" << zaehler << "/" << nenner << ")";
    }
    
    void Bruch::mul(Bruch& b) {
    	zaehler *= b.zaehler;
    	nenner *= b.nenner;
    }
    
    int Bruch::getZaehler() {
    	return zaehler;
    }
    
    int Bruch::getNenner() {
    	return nenner;
    }
    
    Bruch Bruch::operator*(const Bruch& b) {
    	int z = zaehler*b.zaehler;
    	int n = nenner*b.nenner;
    
    	Bruch neu(z,n);
    	return neu;
    }
    
    ostream& operator<<(ostream& os, const Bruch& b) {
    	os << b.getZaehler() << "/" << b.getNenner();
    	return os;
    }
    


  • Was soll der Code jetzt noch?



  • Damit habe ich mich auf deine Aussage

    manni66 schrieb:

    Da du nur Schnipsel zeigst, muss ich raten: es fehlt #include<iostream> im Header.

    bezogen. Daher nochmal der komplette Quellcode der Klasse.

    iostream habe ich eingebunden. Und auch mit std::ostream bekomme ich die selben Meldungen vom Compiler(habe es trotz using namespace std noch Mal extra so probiert).

    EDIT: Okay es war wirklich ein fehlender include von iostream bzw zudem ein fehlen von std::

    Hatte dies nur in der cpp eingebunden.

    Danke für die Antworten 👍



  • Es bleiben noch andere Dinge zu sagen:

    - deine Funktion void mul(Bruch&); sollte einen const Bruch& als Argument nehmen.

    - du hast nun mehrere Operationen, die Brüche multiplizieren. Nämlich operator* und mul . Das mul hat (bis auf den Rückgabetyp) die Semantik des operator*= . Ich würde also vorschlagen, mul zu entfernen und stattdessen den operator*= zu implementieren. Operator* kannst du mit Copy-Constructor+Operator*= implementieren.

    - um den Bruch auszugeben, brauchst du keinen privaten Zugriff. Demnach kannst du auf die friend-Deklaration verzichten.



  • wob schrieb:

    Es bleiben noch andere Dinge zu sagen:

    - deine Funktion void mul(Bruch&); sollte einen const Bruch& als Argument nehmen.

    - du hast nun mehrere Operationen, die Brüche multiplizieren. Nämlich operator* und mul . Das mul hat (bis auf den Rückgabetyp) die Semantik des operator*= . Ich würde also vorschlagen, mul zu entfernen und stattdessen den operator*= zu implementieren. Operator* kannst du mit Copy-Constructor+Operator*= implementieren.

    - um den Bruch auszugeben, brauchst du keinen privaten Zugriff. Demnach kannst du auf die friend-Deklaration verzichten.

    Hi,
    danke für deine Antwort. Das mit der friend-Deklaration ist gut zu wissen. Werde es demnach gleich abändern.

    Die mul-Methode hatte ich ganz am Anfang geschrieben und habe mich dann erst an die operatoren gesetzt. Die Methode kann eigentlich ganz weg. Ist nur ein überbleibsel.

    wob schrieb:

    ...und stattdessen den operator*= zu implementieren. Operator* kannst du mit Copy-Constructor+Operator*= implementieren.

    Das verstehe ich noch nicht so ganz. Vielleicht ergibt sich mir die Logik ja gleich beim schreiben des Codes. Ich wäre (wenn ich jede Operation benötige) so vorgegangen das ich jeden operator: +, +=, *, *= etc einzeln überladen hätte.

    Der Copy-Ctor wäre ja dann dem zu folge:

    Bruch(const Bruch& b) {
       zaehler *= b.zaehler;
       nenner *= b.nenner;
    }
    

    Aber wahrscheinlich denke ich da jetzt komplett falsch 😕



  • bytemare schrieb:

    Der Copy-Ctor wäre ja dann dem zu folge:

    Bruch(const Bruch& b) {
       zaehler *= b.zaehler;
       nenner *= b.nenner;
    }
    

    Aber wahrscheinlich denke ich da jetzt komplett falsch 😕

    Der Copy-Constructor KOPIERT doch nur den Wert. Wenn ich 3/4 kopiere, sind es immer noch 3/4. Da muss nichts multipliziert werden. Der Copy-Ctor ist auch schon automatisch für dich der richtige, d.h. du brauchst ihn nicht zu implementieren.

    Aber der operator+(a,b) kann immer implementiert werden als: kopiere a in a', rechne a'+=b, gib a' zurück. Das gilt allgemein, also auch für -, / und * und unabhängig davon, was für Objekte du hast.

    Also kommt da sowas bei raus:

    Bruch& Bruch::operator*=(const Bruch& b) {
        zaehler *= b.zaehler;
        nenner *= b.nenner;
        return *this;
    }
    
    // * als freie Funktion, nicht als Klassenmember
    Bruch operator*(const Bruch& a, const Bruch& b) {
        Bruch produkt(a); // kopieren
        produkt *= b;     // multiplizieren
        return produkt;
    }
    

    Und mit fällt noch gerade auf, dass getZahler und getNenner als const markiert sein sollten.

    Nächste Aufgabe für dich: Wenn du folgendes Hauptprogramm hast:

    int main() {
        Bruch b(3,4);
        cout << b * b << '\n';
        b*=Bruch(4,3);
        cout << b << '\n';
    }
    

    wäre es schön, wenn statt 9/16 und 12/12 die gekürzten Brüche herauskommen würden, d.h. 9/16 und 1/1.



  • Ah okay dann habe ich es einfach nur falsch verstanden, da das Wort Copy-Ctor gefallen ist dachte ich nämlich sofort an:

    Klasse(const Klasse& obj)
    

    So wie du es gemacht hast, wäre normalerweise auch meine erste Idee gewesen. Wobei du mir evtl. noch Mal erklären könntest wieso dies keine Member-Methode sein darf 😕

    Das mit dem kürzen von Brüchen habe ich schon programmiert, dass war jetzt nur in dieser Klasse nicht vorhanden (GGT-Algo)

    EDIT: So müsste es jetzt stimmen, sry

    int Bruch::ggt() {
          int z = zaehler;
          int n = nenner;
          int r = 0;
    
          while(z > 0) {
                r = n%z
                n = z;
                z = r;
          }
    
          return n;
       }
    

    Ich meine so war es richtig habe es grade nicht offen vor mir liegen. Aber ich werde glaub ich zur Übun die komplette Klasse noch Mal richtig implementieren. Ich kann das Ergebnis ja dann posten falls es dich interessiert. Vllt. fallen dir ja noch weitere Sachen auf die ich verbesssern / ändern kann.

    Gruß



  • bytemare schrieb:

    Ah okay dann habe ich es einfach nur falsch verstanden, da das Wort Copy-Ctor gefallen ist dachte ich nämlich sofort an:

    Klasse(const Klasse& obj)
    

    Ja sicher, genau das ist der Copy-Ctor. Was meinst du, was in der Zeile in meinem letzten Posting passiert, die ich mit "// kopieren" markiert habe?

    Wobei du mir evtl. noch Mal erklären könntest wieso dies keine Member-Methode sein darf 😕

    Das habe ich nicht so gesagt. Ich schlage nur vor, es nicht als Member zu machen. Es wäre aber möglich.
    Grund:
    Vielleicht willst du ja auch mal eine Zahl zu einem Bruch addieren. Also:

    Bruch b(1,2);
    Bruch eineinhalb = b + 1;
    Bruch zweieinhalb = 2 + b;
    

    Das heißt, du brauchst einmal einen Operator, der Bruch+int behandelt und einmal einen der int+Bruch behandelt. Überlege nun weiter, wie du das implementieren kannst.

    int Bruch::ggt() {
    

    Nein nein nein! ggt ist doch eine allgemein gültige Funktion, die nicht mit deinem Bruch zusammenhängt. Folglich sollte es auf keinen Fall eine Funktion sein, die 0 Argumente nimmt. Und sie braucht auch keine Memberfunktion zu sein.

    also: int ggt(int a, int b) oder so.

    Eine einzelne Funktion lässt sich auch viel leichter testen.

    Du könntest ja zum Beispiel mal testen, was bei ggt(8,-4) herauskommt 😉

    Und wenn du einen C++17-Compiler hast, solltest du besser std::gcd verwenden.



  • Hi,
    also ich habe jetzt angefangen die Klasse Bruch noch ein Mal vernünftig zu implementieren. Kann als Übung ja nie schaden.

    Allzu weit bin ich noch nicht und habe direkt einige Fragen.
    Ich bin immer noch bei dem operator<<

    So funktioniert es:

    Bruch.h

    #pragma once
    #include <iostream>
    
    using namespace std;
    
    class Bruch
    {
    
    private:
    	int zaehler, nenner;
    
    public:
    	Bruch(int, int);
    
    	int getZaehler();
    	int getNenner();
    
    	// überladen von operatoren
    	friend ostream& operator<<(ostream& os, const Bruch& b);	// Ausgabe-Stream
    };
    

    Bruch.cpp

    #include "Bruch.h"
    #include <iostream>
    
    using namespace std;
    
    Bruch::Bruch(int z, int n) {
    	zaehler = z;
    	nenner = n;
    }
    
    int Bruch::getZaehler() {
    	return zaehler;
    }
    
    int Bruch::getNenner() {
    	return nenner;
    }
    
    // Operatorn
    ostream& operator<<(ostream& os, const Bruch& b) {
    	os << b.zaehler << "/" << b.nenner;
    	return os;
    }
    

    Nun zu meinen Fragen:
    1. Wieso kann ich hier nur b.zaehler bzw. b.nenner ansprechen? Die getter Methoden werfen imer wieder einen Fehler

    Wenn ich zB die friend deklaration weglasse und in der Methode
    b.getZaehler() bzw. b.getNenner() benutze erfolgt folgende Fehlermeldung:

    In der Header:
    error C2804: Binärer Operator "<<" hat zu viele Parameter

    In der cpp Datei:
    error C2662: "int Bruch::getZaehler(void)": this-Zeiger kann nicht von "const Bruch" in "Bruch &" konvertiert werden

    error C2662: "int Bruch::getNenner(void)": this-Zeiger kann nicht von "const Bruch" in "Bruch &" konvertiert werden

    Füge ich die friend deklarierung wieder hinzu ist zumindest der error in der Header Datei weg. Die Getter-Methoden werden trotzdem nicht angenommen. Wieso nicht?

    Gruß



  • Du hast deine getter nicht const gemacht, dadurch kannst du sie nicht mit const Objekten benutzen.

    Wenn du friend weglässt, darfst du die Funktion nicht in der Klasse definieren, weil es sonst ein Member ist.



  • Okay, vielen Dank. Das const mal wieder 😡
    Jetzt funktioniert es.


Anmelden zum Antworten