benutzerdefinierter << - Operator



  • Guten Abend,
    ich habe ein Problem damit, meinem cout und <<-Operator zu sagen, wie meine Klasse ausgegeben werden soll. Zunächst die Aufgabenstellung vom Prof:
    Ich habe 256 Bits, aufgeteilt in ein int Array, bestehend aus 8*32bits. Dann lese ich ASCII-Zeichen ein, und bei der entsprechenden Nummer wird das Bit von 0 auf 1 gesetzt.
    Bsp.: "ABC" wird eingelesen -> Bits 65,66,67 sollen 1 gesetzt werden.
    Headerdatei:

    enum { NUMCHARS = 256 };               // Umfang des Zeichensatzes
    enum { INTBITS = sizeof(int) * 8 };    // Bits pro int
    enum { ARRSIZE = NUMCHARS / INTBITS }; // Arraygröße
    
    class CharSet
    {
        private:
            int data[ARRSIZE];
        public:
            CharSet(const char *n="");
            bool contains(const char n);
            ~CharSet() {};
    };
    ostream& operator<<(ostream& os, const CharSet& c);
    

    Klasse:

    #include <iostream>
    using namespace std; // HIER auskommentiert bringt Fehler 2
    #include "CharSet.h"
    CharSet::CharSet(const char *n)
    {
        int bit, i, j, shift;
        for(i=0; i<ARRSIZE; i++)
            data[i] = 0;
        for(j=0; j<(signed)strlen(n); j++)
        {
            bit = 1;
            shift = n[j]%INTBITS;
            bit = bit << shift;
            data[n[j]/INTBITS] |= bit;
        }
    }
    ostream& operator<<(ostream& os, const CharSet& c)
    {
        int i, j=0;
        char *arr;
        for(i=0; i<NUMCHARS; i++)
            if(c.contains((char)i)==true)
                arr[j++] = i;
        return os << arr;
    }
    bool CharSet::contains(const char n)
    {
        int i, shift;
    
        i = n/INTBITS;
        shift = n%INTBITS;
        if( ((data[i] >> shift) & 1) ==1 )
            return true;
        else
            return false;
    }
    

    Testdatei:

    #include <iostream>
    using namespace std;
    #include "CharSet.h"
    #define COUT(par) (cout << #par " = " << (par) << '\n')
    int main(void)
    {
       const CharSet cs1("ABCX");
       const CharSet cs2("CDY");
       const CharSet cs3("BC");
       const CharSet cs4(cs1);
       COUT(cs1);
       COUT(cs2);
       COUT(cs3);
       COUT(cs4);
       return 0;
    }
    

    Fehler1: Fehler: Die Übergabe von »const CharSet« als »this«-Argument von »bool CharSet::contains(char)« streicht Qualifizierer
    Wenn Zeile 2 im Klassen-Code auskommentiert wird, gibts den Fehler:
    Fehler2: Fehler: expected constructor, destructor, or type conversion before »&« token

    Ich programmiere unter Ubuntu Linux mit Code::Blocks, und habe eigentlich versucht, das Programm soweit es ging aus dem Skript anzupassen, aber die operator<<-Funktion macht Schwierigkeiten.
    Ich hoffe, mir kann jemand helfen!!
    Vielen Dank schonmal,
    Flip



  • Der erste Fehler bezieht sich auf die const-ness von Methoden.
    Du übergibst eine konstante Referenz auf CharSet im <<-Operator, d.h. es dürfen nur konstante Methoden aufgerufen werden.
    Da die Methode 'contains' ja vom Sinn her konstant ist, brauchst du es einfach angeben:

    bool contains(const char n) const;
    
    // Hinweis: const muß dann auch bei der Definition angegeben werden!!!
    

    Und in den Header-Dateien sollte man immer explizit den Namespace angeben, d.h.

    std::ostream& operator<<(std::ostream& os, const CharSet& c);
    

    Daher kommt der Fehler, wenn man kein "using namespace std; " angibt.

    P.S. Es wäre besser gewesen, wenn du auch noch die Zeilennummern der Fehler gepostet hättest...



  • ah, das tut mir leid mit den Zeilennummern! ganz vergessen, sorry!

    und vielen Dank, das const hat gefehlt! .. dass ein kleines "const" so'n fehler macht, da konnte ich mir nicht viel drunter vorstellen...

    jetzt funktionierts, bis auf eine kleinigkeit in der Ausgabe:

    cs1 = ABCX
    cs2 = CDY
    cs3 = BC
    cs4 = ABCX��������������������������
    

    die ersten 3 werden gut ausgegeben, aber beim vierten gibt er noch einige undefinierte zeichen aus. woran kann das liegen? habe schon ein '\0' eingefügt:

    ostream& operator<<(ostream& os, const CharSet& c)
    {
        int i, j=0;
        char *arr;
        for(i=0; i<NUMCHARS; i++)
            if(c.contains((char)i)==true)
                arr[j++] = i;
        arr[j] = '\0';
        return os << arr;
    }
    

    aber das hat bei der letzten zeile nix geholfen..
    hat jemand eine idee?



  • Ich habe mir nochmal ein paar Gedanken gemacht, und festgestellt, dass bei der vierten Ausgabe durch irgendeinen Grund noch andere Bits gesetzt werden, mit Nummern über 100 und so, also die nach den Buchstaben und Zahlen im ASCII-Code kommen und deswegen nicht dargestellt werden können. Wenn ich in Zeile 5 die Schleife statt bis NUMCHARS(256) nur bis 100 laufen lasse, ist das Problem weg .. aber das ist ja keine Lösung..
    Nur warum passiert das?
    Dazu kommt, was ich hier reingestellt habe, ist zur besseren Übersicht nur ein Teil des Programms, ich bilde auch noch verschiedene Schnittmengen und sowas. Da gibts das gleiche Problem.
    Kann mir jemand sagen was ich da machen kann? Bin echt ratlos!



  • wieso eigtl so komisch?

    #include <cstdlib>
    using std::size_t;
    
    class CharSet
    {
        public:
            static const size_t char_count;
            static const size_t intbits;
            static const size_t length;
    
        private:
            int data[length];
        public:
            CharSet(const char *n=""); //ich würde hier eigtl eher über NULL bzw nullptr gehen, als nen leeren string als standard-parameter zu nehmen
            bool contains(const char n) const;
    #if 0
            ~CharSet() {}; //brauchst du nicht du definieren, wenn er nichts machen muss
    #endif
    };
    
    const size_t CharSet::char_count = 255;
    const size_t CharSet::intbits = sizeof(int)*8;
    const size_t CharSet::length = CharSet::char_count / CharSet::intbits;
    

    das mit den enums ist nicht wirklich schöner...

    dein op << war übrigens falsch 😛

    ostream& operator<<(ostream& os, const CharSet& c) 
    {
        int i, j=0; 
        char *arr; //zeiger zeigt irgendwo hin - du hast dort aber keinen speicher reserviert...
        for(i=0; i<NUMCHARS; i++) 
            if(c.contains((char)i)==true) 
                arr[j++] = i; //peng!
        arr[j] = '\0'; 
        return os << arr; 
    }
    

    also versuchen wir es mal so...

    ostream& operator<<(ostream& os, const CharSet& c) 
    {
        char arr[char_count+1];
    
        size_t j = 0;
        for(size_t i=0; i != char_count; ++i)
        {
            if( c.contains (char (i)) ) //ein cast war hier auch nicht nötig - und zum casten nimmt man in C++ eigtl static_cast(wenn möglich) und (ansonsten) reinterpret_cast
                arr[++j] = i;
        }
        arr[j] = '\0'; 
    
        return os << arr; 
    }
    

    sollte jz gehen ^^
    allerdings hab ich mir die aufgabenstellung nicht durchgelesen und den rest auch nicht - kann sein, dass das alles sehr viel einfacher geht, als du das dort hast ^^
    ich habs jz doch zumindest mal überflogen um dich auf paar hässliche dinge hinzuweisen:

    #include <iostream>
    using namespace std; //in *.cpp dateien ist using namespace iO - brauchst es also net auskommentieren
    
    #include "CharSet.h"
    
    CharSet::CharSet(const char *n)
    {
    #if 0
        int bit, i, j, shift; /*1. ist int hier der falsche datentyp, weil du keinen negativen wertebereich brauchst und
     2. deklariert man (in C++) variablen erst dort, wo sie gebraucht werden (und damit auch meist direkt sinnvoll initialisiert werden können)*/
    #endif
    #if 0 //für so was gibts memset oder data = {0} oder std::fill
        for(size_t i=0; i != length; ++i) //!= ist dem < und preinc dem postincrement vorzuziehen - macht hier zwar performance-mäßig keinen unterschied, aber angewöhnen sollte man sich das trotzdem
            data[i] = 0;
    #endif
    
        std::fill (&data[0], &data[length], 0); //#include <algorithm>
    
        for(size_t i=0, e=strlen(n); i != e; ++i) //i ist wieder frei, also nehmen wir wieder i ^^ und die fkt brauchen wir nicht jeden durchlauf wieder auszuführen (wobei der compiler es sicherlich eh wegoptimieren konnte)
        {
            int bit = 1; //nich genug durchgelesen, um was über den datentyp sagen zu können
            int shift = n[i]%INTBITS; //j durch i ersetzt
            bit = bit << shift;
            data[n[i]/INTBITS] |= bit; //nochmal
        }
    }
    
    ostream& operator<<(ostream& os, const CharSet& c)
    {
    // siehe oben
    }
    
    bool CharSet::contains(const char n)
    {
        const int i = n/intbits;
        const int shift = n%intbits;
    
    #if 0
        if( ((data[i] >> shift) & 1) ==1 )
            return true;
        else
            return false;
    #endif
    
        return static_cast <bool> ( (data[i] >> shift) & 1 ); //das hier kann man mit sicherheit auch noch hübscher schreiben ^^
    }
    

    (void) schreibt man in C++ eigtl nicht mehr in die parameterliste...
    -> int main() {/.../}
    das return 0; am ende der main-fkt ist zwar nicht falsch, aber unnötig - obs unelegant ist oder nicht, ist zwar (vrmtl) ansichtssache, aber ich finds hässlich 😛

    bb

    kannst ja mal drüber gucken (und hoffentlich bissl was ändern) - falls es dann noch immer nicht geht, musste dich halt noch mal melden ^^

    edit:
    ich habe oben using std::size_t; in nem header geschrieben - eigtl macht man das nicht(nie!) - ich machs auch nur bei size_t, weil es imho in c++ der meistgenutzte (weil sinnvollste^^) datentyp ist und damit genau wie int/float usw für mich mit zu den elementaren datentypen gehört - im msvc z.bsp. braucht man das include und den namespace für size_t nicht, aber in compilern, die sich da mehr an den standard halten, braucht man es...



  • Hey unskilled,
    vielen Dank erstmal für deinen Beitrag.
    Das Problem ist leider, dass meine Aufgabenstellung etwas ... einschränkend .. ist. Die Sache ist, ich bin Student und das ganze ist eine Aufgabe für die FH. Da mich das ganze aber interessiert, will ich nicht einfach zum höheren Semester gehen und die Datei kopieren, wenn ich mal ein Problem habe, sondern ich frage mich lieber im Internet durch, damit ich auch noch was dabei lerne. Soweit zur Vorgeschichte.
    Du hast mir ganz viele tolle C++ - Sachen vorgeschlagen, aber leider sollen wir das so machen, wie ich es angefangen habe. Also ohne die tollen Bibliotheken, sondern noch recht C-ähnlich, denn die Vorlesung stellt den Übergang von C zu C++ dar. Wir arbeiten uns quasi Stück für Stück näher an C++ ran. Deswegen "darf" ich z.B. die size_t's nicht verwenden, sondern muss meine enums nehmen.

    Das mit dem Destruktor war mir nicht klar, ich dachte den muss man immer definieren, auch wenn er nix machen muss.
    Die Sache mir dem <<-Operator habe ich versucht umzusetzen, leider ändert sich an meiner "verbuggten" Ausgabe nix..

    ostream& operator<<(ostream& os, const CharSet& c)
    {
        int i, j=0;
        char arr[NUMCHARS];
        for(i=0; i<NUMCHARS; i++)
            if(c.contains((char)i))
                arr[j++] = i;
        arr[j] = '\0';
        return os << arr;
    }
    

    habe auch versucht mit new Speicher zu reservieren, aber auch damit hatte ich trotzdem den gleichen Mist.

    Was kann ich noch versuchen? Mir scheint, es hängt daran, dass ich in der vierten Ausgabe keinen String "ABCX" oder so übergebe, sondern ein CharSet-Element (cs1). Muss ich dafür dann auch einen neuen Konstruktor oder so definieren? habe ich auch schon versucht, aber das hat irgendwie auch nicht geholfen...

    Aaaah!



  • na dann guck ma, was er im copy-ctor macht (und was nicht).

    debugger und so ^^



  • Hm, ehrlich gesagt mit debuggern kenn ich mich nicht so aus (leider), aber hier ist mein Versuch des Kopier-Konstruktors... :

    CharSet::CharSet(const CharSet& c)
    {
        int i;
        for(i=0; i<ARRSIZE; i++)
            data[i] = c.data[i];
    }
    

    Ich habe auch bei der Ausgabe mit hereingepfuscht, dass er alle 8 Int's der Arrays mit ausgeben soll, die sind identisch:
    cs1:

    0
    0
    16777230
    0
    0
    0
    0
    0
    

    cs4 (die Kopie) :

    0
    0
    16777230
    0
    0
    0
    0
    0
    

    Es sollte wirklich identisch sein! Er nutzt auch den Kopier-Konstruktor, das habe ich auch überprüft. Warum habe ich immer noch meine verbuggte Ausgabe?! Ich checks nicht, ehrlich!



  • hmm.. ich fand eigtl auch, dass es ganz brauchbar aussah ^^
    wenn du mir nen compilierbares bsp schickst, was net in mehrere files unterteilt ist (also einmal copy&paste), dann guck ich mal - bin nur zu faul, das gerad alles selbst zu tippen ^^

    bb



  • alles klar 🙂
    hab hier eins zusammenkopiert, da sind zuerst die ausgaben drin, was die einzelnen Ints in den arrays für werte haben, und unten dann die Ausgabe.

    #include <iostream>
    using namespace std;
    #define COUT(par) (cout << #par " = " << (par) << '\n')
    
    enum { NUMCHARS = 256 };               // Umfang des Zeichensatzes
    enum { INTBITS = sizeof(int) * 8 };    // Bits pro int
    enum { ARRSIZE = NUMCHARS / INTBITS }; // Arraygröße
    
    class CharSet
    {
        private:
            int data[ARRSIZE];
        public:
            CharSet(const char *n=NULL);
            CharSet(const CharSet& c);
            bool contains(const char n) const;
            ~CharSet() {};
    };
    ostream& operator<<(ostream& os, const CharSet& c);
    
    int main(void)
    {
       const CharSet cs1("ABCX");
       const CharSet cs2("CDY");
       const CharSet cs3("BC");
       const CharSet cs4(cs1);
       COUT(cs1);
       COUT(cs2);
       COUT(cs3);
       COUT(cs4);
       return 0;
    }
    
    CharSet::CharSet(const char *n)
    {
        int bit, i, j, shift;
        for(i=0; i<ARRSIZE; i++)
            data[i] = 0;
        for(j=0; j<(signed)strlen(n); j++)
        {
            bit = 1;
            shift = n[j]%INTBITS;
            bit = bit << shift;
            data[n[j]/INTBITS] |= bit;
        }
            for(i=0; i<ARRSIZE; i++)
                cout << data[i] << "\n";
    }
    
    CharSet::CharSet(const CharSet& c)
    {
        int i;
        for(i=0; i<ARRSIZE; i++)
        {
            data[i] = c.data[i];
            cout << data[i] << "\n";
        }
    
    }
    
    ostream& operator<<(ostream& os, const CharSet& c)
    {
        int i, j=0;
        char arr[NUMCHARS];
        for(i=0; i<NUMCHARS; i++)
            if(c.contains((char)i))
                arr[j++] = i;
        arr[j] = '\0';
        return os << arr;
    }
    
    bool CharSet::contains(const char n) const
    {
        int i, shift;
    
        i = n/INTBITS;
        shift = n%INTBITS;
        if(data[i]==0)
            return false;
        else
        {
            if( ((data[i] >> shift) & 1) ==1 )
                return true;
            else
                return false;
        }
    }
    

    (Das schlimmste wär jetzt, wenns bei dir fehlerfrei läuft...)
    Vielen Dank fürs ausprobiern!!
    ~flip



  • also zu erst mal:
    der fehler tritt _immer_ auf - ich guck auch gleich mal, wieso genau - davor aber noch was: wenn du den ctor mit const char* x = NULL deklarierst, musst du (bevor du strlen aufrufst schon mal gucken, ob du nen gültigen pointer hast, weil strlen nich auf null prüft) - wenn ich das jz so sehe, wohl doch nen extra ctor für den fall bauen...

    also in etwa so:

    charset()
    {
     nullfkt();
    }
    
    charset(const char* val)
    {
     nullfkt();
    
    for(j=0; j<(signed)strlen(n); j++)
        {
            bit = 1;
            shift = n[j]%INTBITS
    }
    

    das der fehler immer auftritt liegt daran, dass die contains() fkt nicht richtig funktioniert...
    gibt mehrere (mögliche) gründe dafür:
    - char ist nicht zwangsläufig auch signed -> du kannst eben nich einfach von nem int auf nen char casten -> bin mir nicht sicher, in wie fern man rumcasten kann, da ich selbst eigtl fast keine casts brauche

    oder was noch wahrscheinlicher ist
    - deine shift-operation is iwie falsch, allerdings kann ich da auf den ersten blick nichts erkennen...

    bb

    edit:
    ok - war wirklich das problem mit dem cast...

    ostream& operator<<(ostream& os, const CharSet& c) 
    { 
        char i, j=0;
        char arr[NUMCHARS]; 
        for(i=0; i!=-128+NUMCHARS; i++)
    	{
            if(c.contains(i)) 
                arr[j++] = i;
    	}
        arr[j] = '\0'; 
        return os << arr; 
    }
    

    funktioniert...
    ich würd jz (wenn ich soetwas komisches machen würde) iwas mit numeric_limits<char>::min()+NUMCHARS machen, denk ich... allerdings müsste man davor halt noch nen assert machen, dass NUMCHARS nicht größer ist, als betrag(min_char) + betrag(max_char)...

    bb



  • Also ich vermute du hast recht, dass die shift-Operator-Funktion falsch ist. Ich habe eben doch mal einen Versuch mit dem Debugger gestartet, und die Schleife in der Operator-Fkt. (Zeile 74) Step für Step durchgetackert. Er hat auch alles schön gemacht und in arr reingeschrieben, aber auf einmal (so bei ca. 100), wo er eigentlich keine "1"er mehr finden dürfte, waren da auf einmal welche!



  • Ah! wahnsinn, es geht!!
    sitze seit fast 24 stunden da dran!!
    Vielen DANK! Aber warum er da so einen Mist macht, ist mir immer noch nicht ganz klar ...



  • siehe edit ^^



  • Ja, wie gesagt, es läuft!

    Aber:
    Im Grunde läuft er doch jetzt nur bis 128! Weil -128+NUMCHARS(=256) = 128. Aber das Array data hat doch 8x32=256 bits. Bin ich da nicht am "mogeln", wenn ich eigentlich nur bis 128 abfrage?



  • huch xD hatte net mehr an die 0 gedacht xD
    japp, du schummelst 😃

    der cast is au net das eigtl problem, weil er dann ja nat. nur die reihenfolge vertauscht...
    also 0,1,2,...,126,127,-128,-127,...,-1

    jedenfalls geht das hier nicht (und er probiert nicht alle zeichen durch sondern lässt das letzte weg):

    #include <limits>
    
    ostream& operator<<(ostream& os, const CharSet& c) 
    { 
        char i; size_t j=0;
        char arr[NUMCHARS]; 
    	for(i=std::numeric_limits<char>::min(); i!=std::numeric_limits<char>::max(); i++)
    	{
            if(c.contains(i)) 
                arr[j++] = i;
    	}
        arr[j] = '\0'; 
        return os << arr; 
    }
    

    also wirst du mal suchen müssen, was du beim shiften falsch machst 😛

    bb



  • hey,
    ich hab nochmal geschaut, aber ich find nix mehr ..
    ich denk ich wackel mal so damit hin zu meinem prof, es läuft ja im Grunde. soll der mir mal erklären was da noch falsch ist. So!

    Vielen Dank nochmal!!

    Gruß, flip


Anmelden zum Antworten