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



  • 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.



  • hustbear schrieb:

    Philipp2706 schrieb:
    Ich frage mich jetzt nur was ich mit der 2 in:
    C++:
    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.

    Stimmt schon, eigentlich logisch 🙄

    hustbear schrieb:

    Diese Stellen musst du jetzt bloss noch von einem Integer (0...35) in ein Zeichen ('0'...'9', 'a'...'z') verwandeln,

    Dazu ist mir nur eingefallen ein enum mit den zeichen 0 bis z zu erstellen. Das sieht vieleicht etwas seltsam aus, aber es funktioniert (fast) 😃
    Kann man das so machen?

    Bei Basis 36 wird ZSI ausgegeben, es müsste aber YRI sein. Ich kann mir nicht erklären warum 😕 😕

    //Zeichen enum
    enum ZeichenTyp {
        Null,Eins,Zwei,Drei,Vier,Fuenf,Sechs,Sieben,Acht,Neun,
        A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
    };
    typedef ZeichenTyp zeichen;
    string zeichenStr[] = {"0","1","2","3",
    "4","5","6","7","8","9","A","B","C","D","E","F","G",
    "H","I","J","K","L","M","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
    
    void GanzeZahl::konvertiereNachBasis(int basisKonv) 
    { 
         zeichen aktuellesZeichenEnum;   
         unsigned int faktor=1, dezimalInput;  
         string aktuellesZeichenStr="", aktuellesZeichenRichtig="";
         dezimalInput= dezWert; 
    
         while (dezimalInput) 
             { 
             aktuellesZeichenEnum= (zeichen) (dezimalInput % basisKonv);
             aktuellesZeichenStr += zeichenStr[aktuellesZeichenEnum];
    
             dezimalInput = dezimalInput / basisKonv;
             faktor *= 10;     
             } 
    
         //String umdrehen
    	for (int i =aktuellesZeichenStr.size(); i>0; i--)
    	{
            aktuellesZeichenRichtig = aktuellesZeichenRichtig + aktuellesZeichenStr[i-1];
    	}
    
         this ->ziffernBasis = aktuellesZeichenRichtig;
         } 
    /*
    

    Dieser schöne Ansatz mit stol ist leider nicht erlaubt. Wir sollen zuerst in eine Zehnerpotenz und danach in die beliebige Potenz umrechnen.

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

    Dazu würde ich gerne das Horner-Schema implementieren. Und für die Koeffizienten mein enum von oben verwenden. Bzw den Typ zeichen.

    Ich habe ein bisschen herumexperimentiert aber ich bekomme immer eine Fehlermeldung. "invalid conversion from int to zeichen...."

    Ich habe gelesen dass die Konvertierung von string zu enum nicht so einfach möglich ist. Aber ich würde es praktisch finden wenn beide Methoden das enum verwenden können.
    Ist das irgendwie möglich?

    //Neue Operatorüberladung mit Hornerschema
        ostream &operator<<(ostream &ostr, const GanzeZahl& Zahl) {
    
        int n =Zahl.ziffernBasis.length();
        zeichen ergebnis;
        zeichen zeichenArr [n];
        zeichen basisZeich = (zeichen)Zahl.basis;
    
        //String in ein zeichen-Array kopieren 
        for (int i=0;i<Zahl.ziffernBasis.length();i++)
        zeichenArr [i]==Zahl.ziffernBasis[i];
    
        //Horner-Schema
        for(int i=n-1; i >= 0 ; --i)
        ergebnis = ergebnis * basisZeich + zeichenArr[i];
    
        return ergebnis;}
    


  • Philipp2706 schrieb:

    Dazu ist mir nur eingefallen ein enum mit den zeichen 0 bis z zu erstellen. Das sieht vieleicht etwas seltsam aus, aber es funktioniert (fast) 😃
    Kann man das so machen?

    Klar, kann man. Der enum bewirkt allerdings gar nix, das ist dir klar, oder?
    Und es geht einfacher. Guck dir mal die numerischen Werte der Zeichen '0'...'9' sowie 'a'...'z' an.

    Philipp2706 schrieb:

    Bei Basis 36 wird ZSI ausgegeben, es müsste aber YRI sein. Ich kann mir nicht erklären warum 😕 😕

    Was wäre die einfachste Erklärung? Dass du nen Buchstaben ausgelassen hast vielleicht? Und genau daran liegt es auch.

    Philipp2706 schrieb:

    Dieser schöne Ansatz mit stol ist leider nicht erlaubt.
    ...
    Dazu würde ich gerne das Horner-Schema implementieren. Und für die Koeffizienten mein enum von oben verwenden. Bzw den Typ zeichen.

    Auch hier der Tip: guck dir die numerischen Werte der verschiedenen Zeichen an. ASCII Tabelle und so.
    Ansonsten tut's aber auch ein switch:

    char ch = ...
    switch (ch) {
        case '0':
            ...
        case '1':
            ...
        case '2':
            ...
        ...
    

    Philipp2706 schrieb:

    Ich habe ein bisschen herumexperimentiert aber ich bekomme immer eine Fehlermeldung. "invalid conversion from int to zeichen...."

    Ja, das ist in C++ einfach so. Wenn du in dieser Richtung konvertieren willst musst du explizit casten.
    Der enum tut aber wie ich oben schon geschrieben habe nichts. Du scheinst aber anzunehmen dass er etwas tut. D.h. ich vermute dass du enums falsch verstanden hast 😉


Anmelden zum Antworten