<< Operatorüberladung ohne die .cpp zu ändern



  • Hallo zusammen, 🙂
    ich habe folgende Aufgabe und wollte wissen ob mein Ansatz richtig ist. Ich bin was das Thema Klassen angeht noch sehr unwissend.

    Es ist eine .cpp gegeben und wir müssen eine Klasse dazu schreiben.

    GanzeZahl deziZahl(16,"Affe");    // Basis und Ziffern vorgegeben
      cout << deziZahl << endl;         // Dezimalzahl wird ausgegeben
    

    in der Ganzezahl.h habe ich jetzt den Operator dazu geschrieben

    GanzeZahl (const int basis, const string ziffernBasis):
            basis(basis),
            ziffernBasis(ziffernBasis)
    
            {
            int dezimalZahl = stoi(ziffernBasis); 
            }
    

    Mein Problem ist jetz folgendes: Ich muss ja an eine Eigenschaft des Objekts deziZahlen kommen um es auszugeben.
    In der vorgegebenen .cpp Datei steht allerdings nur cout << deziZahl;
    Meine Idee war den << Operator zu überladen, ich habe allerdings nur Lösungen gefunden wobei man die .cpp Datei auch bearbeiten muss. Das darf ich ja nicht.

    Ist mein Ansatz mit dem Überladen überhaupt richtig? 😕



  • ich habe allerdings nur Lösungen gefunden wobei man die .cpp Datei auch bearbeiten muss

    Das kann ich mir nicht vorstellen.

    Du kannst den Operator im Header definieren.

    int dezimalZahl = stoi(ziffernBasis);
    

    Ist das nicht ziemlich nutzlos?



  • manni66 schrieb:

    Zitat:
    ich habe allerdings nur Lösungen gefunden wobei man die .cpp Datei auch bearbeiten muss

    Das kann ich mir nicht vorstellen.

    Du kannst den Operator im Header definieren.

    Gutes Stichwort 😋 http://public.beuth-hochschule.de/~kempfer/skript_cpp/Kap11.html
    Ich habe den Operator jetzt durch die friends-funktion überladen.

    manni66 schrieb:

    C++:
    int dezimalZahl = stoi(ziffernBasis);

    Ist das nicht ziemlich nutzlos?

    Du meinst weil ich von hexadezimal zu dezimal umrechnen will oder?
    Und weil ein Operator nichts zurückgeben kann denke ich.

    Ich habe die Umrechnung jetzt mittels stringstream und >> hex >> gelöst. Dazu habe ich eine eigene Methode geschrieben.

    int hexZuDez (string ziffernBasis){
        int hexInt, dezInt;
        stringstream stream;
        ziffernBasis >> hexInt;
        stream << hexInt;
        stream >> hex >> dezInt;
        return dezInt;
        }
    
    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << ref.dezimalZahl;
        return ostr;
    }
    

    Allerdings muss ich doch immer eine Eigenschaft/Variable mit diesem &ref verbinden oder?
    ich weiß jetzt nicht wie ich den Rückgabewert der Funktion mit &ref verbinden soll 😕



  • Philipp2706 schrieb:

    manni66 schrieb:

    C++:
    int dezimalZahl = stoi(ziffernBasis);

    Ist das nicht ziemlich nutzlos?

    Du meinst weil ich von hexadezimal zu dezimal umrechnen will oder?

    Vermutlich eher, weil "dezimalZahl" in dem gezeigten Scope nicht weiter verwendet wird. Von "hexadezimal" war in deinen Posts bisher keine Rede

    Philipp2706 schrieb:

    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << ref.dezimalZahl;
        return ostr;
    }
    

    Allerdings muss ich doch immer eine Eigenschaft/Variable mit diesem &ref verbinden oder?
    ich weiß jetzt nicht wie ich den Rückgabewert der Funktion mit &ref verbinden soll 😕

    Was wills du da "verbinden"? Was soll der Rückgabewert mit "ref" zu tun haben?



  • daddy_felix schrieb:

    Vermutlich eher, weil "dezimalZahl" in dem gezeigten Scope nicht weiter verwendet wird. Von "hexadezimal" war in deinen Posts bisher keine Rede

    Also der Teil des Programms soll eine Basis und die Zifferndarstellung bezüglich dieser Basis speichern und die Dezimalzahl ausgeben.
    Da war ich etwas zu schnell 😃

    daddy_felix schrieb:

    Was wills du da "verbinden"? Was soll der Rückgabewert mit "ref" zu tun haben?

    das ist ja mein Problem. So wie es auf der Website ist, verstehe ich es. Nur in dem Beispiel wird einfach eine Eigenschaft/Variable ausgegeben. Ich sehe nicht wo da irgendwas gerechnet wird. 😕
    Und meine Frage ist jetzt: Wie kann man etwas berechnen und dann "dem ostream übergeben". Beziehungsweise den Rückgabewert(das Ergebnis) einer Methode dem ostream übergeben.
    "Verbinden" ist das falsche Wort.

    Hier nochmal das Beispiel der Website:

    Quelle: beuth-hochschule schrieb:

    #include <iostream>
    
    using namespace std;
    
    class complex
    {
       private:
          double re,im; // Real- und Imaginärteil
       public:
          complex(): re(0), im(0) {}
          complex(double r, double i): re(r), im(i) {}
          friend complex operator+ (const complex &a, const complex &b);
          friend ostream &operator<< (ostream &ostr, const complex &a);
          friend istream &operator>> (istream &istr, complex &a);
    };
    
    complex operator+ (const complex &a, const complex &b)
    {
       complex c;
    
       c.re = a.re + b.re;
       c.im = a.im + b.im;
       return c;
    }
    
    ostream &operator<< (ostream &ostr, const complex &a)
    {
       ostr << '(' << a.re << " + i * " << a.im << ')';
       return ostr;
    }
    
    istream &operator>> (istream &istr, complex &a)
    {
       istr >> a.re >> a.im;
       return istr;
    }
    
    int main()
    {
       complex a(1,1), b(2,2), c;
    
       c = a + b;
       cout << c << endl;
    
       return 0;
    }
    

    http://public.beuth-hochschule.de/~kempfer/skript_cpp/Kap11.html



  • operator<< ist eine Funktion. Darin kann man rechnen. Was ist das Problem?



  • ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << (int)strtol(ref.ziffernBasis.c_str(), 0, 16);
        return ostr;
    }
    

    Problem gelöst 🙂 👍



  • Philipp2706 schrieb:

    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << (int)strtol(ref.ziffernBasis.c_str(), 0, 16);
        return ostr;
    }
    

    Das ist genau so falsch wie

    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << (int)strtol("Affe", 0, 16);
        return ostr;
    }
    

    falsch wäre.



  • hmm
    wieso passt das Ergebnis dann? 😕



  • Gute Frage 😉

    Was ist das Ziel deiner Klasse? Von den Parametern würde ich darauf tippen, dass du eine Zeichenfolge in eine Zahl umrechnen möchtest, basierend auf der angegebenen Basis (dual, octal, hexal System, oder was einem noch so alles einfallen könnte...)

    Erklär doch mal, was die Zeile

    ostr << (int)strtol(ref.ziffernBasis.c_str(), 0, 16);
    

    macht. Und zwar feinschrittig.



  • okay also die funktion strtol() bekommt 3 Parameter.
    Der erste ist der umzuwandelnde String, der zweite ist der endptr, also die Referenz auf ein char bis zu dem die Konvertierung läuft (so habe ich das verstanden), und der dritte gibt den Zahlenbereich an.
    Hier war denke ich mein Fehler 😃

    ich hab es jetzt zu:

    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << (int)strtol(ref.ziffernBasis.c_str(), 0, 10);
        return ostr;
    }
    

    geändert.

    Schlangenmensch schrieb:

    Was ist das Ziel deiner Klasse? Von den Parametern würde ich darauf tippen, dass du eine Zeichenfolge in eine Zahl umrechnen möchtest, basierend auf der angegebenen Basis (dual, octal, hexal System, oder was einem noch so alles einfallen könnte...)

    Jo genau. Wir haben ein Programm vorgegeben und müssen dazu die Klasse schreiben.
    Ich habe jetzt ersteinmal die erste Teilaufgabe gelöst und alles andere auskommentiert.

    edit:
    Nein das strtol() passt immer noch nicht. Beziehungsweise es gibt nur im Zahlenbereich 16 etwas aus.



  • Jetzt hast du erklärt was strol macht.

    Was soll das (int) davor machen?

    Und, deine Klasse soll das, was in GanzeZahl::ZiffernBasis steht in eine Zahl im Dezimalsystem (?) umwandeln. Warum benutzt du in der Ausgabe eine Funktion die nochmal was konvertiert, sollte doch schon geschehen sein, oder?

    Vielleicht nochmal anders gefragt: Was genau willst du ausgeben? Und wie möchtest du das erreichen (in Textform)

    Wenn du dich über die Ausgabe von bestimmten Werten wunderst, musst du schon die Eingabe posten, also zu deinem Operator die Klasse GanzeZahl und die entsprechenden Parameter die du übergibst.

    Und noch eine C++ spezifische Anmerkung: strtol ist eine C Funktion. C++ kann das auch: http://en.cppreference.com/w/cpp/string/basic_string/stol



  • Philipp2706 schrieb:

    ich hab es jetzt zu:

    ostream &operator<<(ostream &ostr, const GanzeZahl &ref) {
        ostr << (int)strtol(ref.ziffernBasis.c_str(), 0, 10);
        return ostr;
    }
    

    geändert.

    Jetzt ist es noch mehr falsch.
    Vorher hat wenigstens noch das Ergebnis für den einen Fall im Beispielcode gepasst.

    K.A. mit welchem Zaunpfahl ich noch winken könnte, also sag ich's dir einfach direkt: Du solltest nicht bloss den String abspeichern sondern auch die Basis in welcher dieser zu interpretieren ist. Weil so wie du es geschrieben hast sonst immer 16 genommen wird, egal was der Aufrufer angibt.

    Gäbe aber ne noch einfachere Möglichkeit. Schliesslich interessiert es nach der Initialisierung der "ganzen zahl" nicht mehr wirklich wie der Zahlenwert zustande gekommen ist. Also ob es "15" als Dezimalzahl interpretiert war oder "F" als Hex interpretiert.



  • ostream &operator<<(ostream &ostr, const GanzeZahl &Zahl) {
    
        ostr << stol(Zahl.ziffernBasis.c_str(), 0, Zahl.basis);
        return ostr;
    }
    
    }
    

    du meinst das so oder?

    Schlangenmensch schrieb:

    Und, deine Klasse soll das, was in GanzeZahl::ZiffernBasis steht in eine Zahl im Dezimalsystem (?) umwandeln. Warum benutzt du in der Ausgabe eine Funktion die nochmal was konvertiert, sollte doch schon geschehen sein, oder?

    Vielleicht nochmal anders gefragt: Was genau willst du ausgeben? Und wie möchtest du das erreichen (in Textform)

    Okay. Also ich schreibe die Klasse zuerst nur für folgenden Teil des "unveränderbaren" Programms:

    int main() {
      GanzeZahl zahl(16,"Affe");    // Vorgabe von Basis und Ziffern
      cout << zahl << endl;         // Ausgabe als Dezimalzahl
    
    zahl.konvertiereNachBasis(2); // Berechne Binaerdarstellung
    cout << zahl.ausgabeZiffern() << endl; // Ausgabe als Binaerzahl
    

    Die Operatorüberladung und die Ausgabe für den ersten Teil habe ich jetzt hingekriegt.
    Jetzt muss ich diese Dezimalzahl in Binärdarstellung speichern. Die 2 ist der Parameter für die Basis.
    Dazu habe ich einen zweiten Konstruktor geschrieben.
    Für die Konvertierung von Dezimal zu Binär habe ich eine eigene Klasse geschrieben.

    Das Problem vor welchem ich jetzt gerade stehe ist die Binärzahl in einer anderen Methode auszugeben.
    Ich habe schon ein bisschen mit dem this Zeiger herumprobiert. Ist das der richtige Ansatz?

    Und ich habe noch eine Frage zu den Methoden. Die Methoden stehen immer im public bereich oder?
    Im private Bereich stehen doch nur die Eigenschaften/Variablen? 😕

    Vielen Dank bis hierhin 😋 👍
    Hier ist meine Header Datei:

    #ifndef GANZEZAHL_H
    #define GANZEZAHL_H
    
    #include <iostream>
    #include <sstream>
    #include <cstdlib>
    #include <string>
    #include <cmath> 
    
    using namespace std;
    
    class GanzeZahl {
    
        int binBasis;
        int basis;
        string ziffernBasis;
        unsigned long int dezimalZahl;
        int binZahlInt;
    
    public:
    
        int konvertiereNachBasis(int basis) {   
        int rest, ergebnis=0, faktor=1, dezimalInput;
    
            dezimalInput= dezimalZahl;
    	while (dezimalInput)
    		{
    		rest = dezimalInput % 2;
    		dezimalInput = dezimalInput / 2;
    
                    faktor *= 10;
     		ergebnis = ergebnis + rest * faktor;
    		}		
    	return binZahlInt;
        }
    
        int ausgabeZiffern(){
            return this->konvertiereNachBasis(basis);
        }
    
        GanzeZahl (const int basis, const string ziffernBasis):  
        basis(basis),
        ziffernBasis(ziffernBasis){}  
        GanzeZahl (const int binBasis):
        binBasis(binBasis) {} 
        friend ostream& operator<<(ostream& os, const GanzeZahl &zahl);    
    };
    
    ostream &operator<<(ostream &ostr, const GanzeZahl &Zahl) {
        int dezimalZahl;
        ostr << stol(Zahl.ziffernBasis.c_str(), 0, Zahl.basis);
        dezimalZahl=ostr.str();
        return dezimalZahl;
    }
    
    #endif /* GANZEZAHL_H */
    

    Soo also das oben ist jetzt die aktualisierte Header Datei. Ich habe jetzt die Methode konvertiereNachBasis(basis) in ausgabeZiffern() mittels this Zeiger "kopiert".
    Allerdings wird mir jetzt -12816 angezeigt.
    Das ist auch kein Wunder, weil dezimalZahl noch nicht initialisiert wurde.
    Ich muss jetzt irgendwie den Rückgabewert von:

    ostream &operator<<(ostream &ostr, const GanzeZahl &Zahl) {
        int dezimalZahl;
        ostr << stol(Zahl.ziffernBasis.c_str(), 0, Zahl.basis);
        dezimalZahl=ostr.str();
        return dezimalZahl;
    }
    

    in die Eigenschaft/Variable dezimalZahl bekommen 😕



  • So noch einmal meine aktuelle Header Datei.
    Für heute ist Feierabend 🕶 bis morgen 🙂

    #ifndef GANZEZAHL_H
    #define GANZEZAHL_H
    
    #include <iostream>
    #include <sstream>
    #include <cstdlib>
    #include <string>
    #include <cmath> 
    
    using namespace std;
    
    class GanzeZahl {
    
        int binBasis;
        int basis;
        string ziffernBasis;
        unsigned long int dezimalZahl;
        int binZahlInt;
    
    public:
    
        long unsigned int konvertiereNachBasis(long unsigned int binBasis) {   
        long unsigned int rest, ergebnis=0, faktor=1, dezimalInput;
    
            dezimalInput= 45045;
    	while (dezimalInput)
    		{
    		rest = dezimalInput % binBasis;
    		dezimalInput = dezimalInput / binBasis;
    
                    faktor *= 10;
     		ergebnis = ergebnis + rest * faktor;
    		}		
    	return ergebnis;
        }
    
        int ausgabeZiffern(){
            return this->konvertiereNachBasis(binBasis);
        }
    
        GanzeZahl (const int basis, const string ziffernBasis):  
        basis(basis),
        ziffernBasis(ziffernBasis){}  
        GanzeZahl (const int binBasis):
        binBasis(binBasis) {} 
        friend ostream& operator<<(ostream& os, const GanzeZahl &zahl);    
    };
    
    ostream &operator<<(ostream &ostr, const GanzeZahl &Zahl) {
        int dezimalZahl;
        ostr << stol(Zahl.ziffernBasis.c_str(), 0, Zahl.basis);
        //dezimalZahl=ostr.str();
        //return dezimalZahl;
        return ostr;
    }
    
    #endif /* GANZEZAHL_H */
    


  • Philipp2706 schrieb:

    ostream &operator<<(ostream &ostr, const GanzeZahl &Zahl) {
     
        ostr << stol(Zahl.ziffernBasis.c_str(), 0, Zahl.basis);
        return ostr;
    }
        
    }
    

    du meinst das so oder?

    Bingo 🙂

    Was den Rest angeht machst du aber alles viel zu kompliziert.

    Vielleicht solltest du nochmal ganz von vorne anfangen.

    Überleg dir

    1. Was genau soll die Klasse darstellen. Also aus was "bestehen" die Objekte? Bestehen die wirklich aus 5 Teilen? Und was bedeuten die einzelnen Teile?
    2. Welche Konstruktoren wird die Klasse haben?
      - Welche werden für das Beispiel benötigt?
    3. Was ist mit der Funktion ausgabeZiffern wohl gemeint?
      - Was, wenn überhaupt etwas, sollte sie zurückgeben?
      - Was für Nebeneffekte, wenn überhaupt irgendwelche, sollte sie haben?
    4. Was ist mit der Funktion konvertiereNachBasis wohl gemeint?
      - Was, wenn überhaupt etwas, sollte sie zurückgeben?
      - Was für Nebeneffekte, wenn überhaupt irgendwelche, sollte sie haben?

    Zu all diesen Fragen guck dir die Verwendung der Klasse im vorgegebenen Code an plus die Kommentare die dabei stehen.

    Mit den Antworten auf diese Fragen kannst du dann im Prinzip die ganze Klasse in max. 5-10 Minuten hinschreiben (und wenn du schnell bist viel schneller ;)).
    Also erstmal ausgenommen der Implementierung der ganzen Funktionen.
    Und dann implementierst du in aller Ruhe die paar Funktionen, und schwupps bist du schon fertig.



  • husbaers Antwort ist eigentlich nichts hinzuzufügen.

    Aber zu einer Frage wollte ich davon abgesehen noch antworten 😉

    Philipp2706 schrieb:

    Und ich habe noch eine Frage zu den Methoden. Die Methoden stehen immer im public bereich oder?
    Im private Bereich stehen doch nur die Eigenschaften/Variablen? 😕

    Nein, Methoden können auch privat sein, ebenso können Variablen im public bereich stehen.

    Die Frage die sich dafür stellt ist: Soll die Funktion / die Variable von außerhalb der Klasse erreichbar/veränderbar sein. Wenn ja, dann public, sonst private.

    Als drittes gäbe es noch protected. Das spielt aber erst eine Rolle, wenn du Vererbung benutzt. Ansonsten ist es für die Klasse selbst wie Private.



  • hustbaer schrieb:

    1. Was genau soll die Klasse darstellen. Also aus was "bestehen" die Objekte? Bestehen die wirklich aus 5 Teilen? Und was bedeuten die einzelnen Teile?

    An der Stelle hat es bei mir Klick gemacht. 👍 👍 Das Objekt Zahl besteht aus der Basis (int) und den dazugehörigen Zeichen (string). Also zwei Eigenschaften.

    Ihr habt mir echt wieder mal richtig weitergeholfen. 👍

    Die Konvertierung von dem hexadezimal-String zu dem Binärstring habe ich mittels for schleife und switch case gelöst.
    Am Ende ändere ich die Variable (ziffernBasis) die in der Initialisierungsliste initialisiert wurde in den Binärstring.

    this ->ziffernBasis = stringRueckgabe;
    

    In der anderen Methode gebe ich ziffernBasis an die Main

    string GanzeZahl::ausgabeZiffern() const {
            return ziffernBasis;}
    

    Funktioniert alles wie es soll 🙂

    Ich frage mich jetzt nur was ich mit der 2 in:

    zahl.konvertiereNachBasis(2); // Berechne Binaerdarstellung
    

    anstellen soll 😕

    Ich werde morgen meinen Professor fragen, aber meint ihr ich muss die Zahl in beliebige Basen konvertieren?
    Wäre das nicht extrem aufwendig? Ich meine da steht schließlich

    // Berechne Binaerdarstellung

    😕



  • Philipp2706 schrieb:

    Ihr habt mir echt wieder mal richtig weitergeholfen. 👍
    ...
    Funktioniert alles wie es soll 🙂

    Schön. Freut mich dass ich dir helfen konnte dir selbst zu helfen sozusagen 🙂

    Philipp2706 schrieb:

    Ich frage mich jetzt nur was ich mit der 2 in:

    zahl.konvertiereNachBasis(2); // Berechne Binaerdarstellung
    

    anstellen soll 😕

    Ich werde morgen meinen Professor fragen, aber meint ihr ich muss die Zahl in beliebige Basen konvertieren?

    Ich würde sagen: von beliebiger Basis in beliebige Basis.

    Philipp2706 schrieb:

    Wäre das nicht extrem aufwendig? Ich meine da steht schließlich

    // Berechne Binaerdarstellung

    😕

    Ja, das steht da, als Kommentar was die Zeile tut (und das soll sie dann ja auch tun).
    So wie der Code da steht, mit dem Argument "2" für die Funktion, ist aber ziemlich klar dass die Funktion nicht nur in die Binärdarstellung konvertieren können soll. Sondern dass die "Ziel-Basis" über das übergebene Argument gesteuert wird.

    Die Umwandlung ist auch nicht wirklich sehr schwer. Zumindest nicht wenn du sie in zwei Schritten machst. Nämlich erstmal Umwandlung string -> long und dann long -> string.
    Die Umwandlung string -> long hast du ja schon (operator <<).
    Fehlt bloss noch die Umwandlung long -> string mit beliebiger Basis.

    Dafür gibt es dummerweise keine fertige Funktion. Ist allerdings auch die einfachere Richtung.
    Und du hast den Code dafür ja schon fast...

    Philipp2706 schrieb:

    // -snip-
        
        long unsigned int konvertiereNachBasis(long unsigned int binBasis) {   
        long unsigned int rest, ergebnis=0, faktor=1, dezimalInput;
    	
            dezimalInput= 45045;
    	while (dezimalInput)
    		{
    		rest = dezimalInput % binBasis;
    		dezimalInput = dezimalInput / binBasis;
    		
                    faktor *= 10;
     		ergebnis = ergebnis + rest * faktor;
    		}		
    	return ergebnis;
        }
        
    // -snip-
    

    Hier bekommst du ja schon pro Schleifendurchlauf eine Stelle des Strings raus (in der Variable "rest").
    Diese Stellen musst du jetzt bloss noch von einem Integer (0...35) in ein Zeichen ('0'...'9', 'a'...'z') verwandeln, und daraus dann in der richtigen Reihenfolge nen String zusammenbauen.



  • Philipp2706 schrieb:

    .

    GanzeZahl (const int basis, const string ziffernBasis):
    

    Muss so viel const correctness wirklich sein? Der Compiler/Linker wird es sowieso erkennen und optimieren wenn du int nicht verändert wird oder? Es sieht einfach nicht richtig aus. Das zweite Argument würde ich zumindest als const Referenz übergeben.


Anmelden zum Antworten