Operatorenüberladung



    1. Wie ich schon sagte, die ursprüngliche Version verschwindet nicht.
    CVektor a, b, c;
    int x, y, z;
    
    c = a + b;
    z = x + y;
    

    beides funktioniert, weil der Compiler je nach Kontext (== Typen der Operanden) den richtigen Operator auswählt.

    1. Das was ich gesagt habe ist so direkt nur für binäre Operatoren anwendbar (dh +, -, *, /, %, &, |, ^, <<, >>, <, >, <=, >=, ==, !=, &&, ||). Unäre Operatoren (--, ++, *, &, -, (), [], ~, !, cast, ...) funktionieren ähnlich. Der ternäre ?: Operator kann nicht überladen werden. Für Zuweisung, new, delete und -> gibt es Sonderregeln. Und wahrscheinlich hab ich wieder die Hälfte vergessen.

    2. Du überlädst einen Operator in der Regel dann, wenn die Operation, die er ausführt, klarer durch einen Operator als durch eine Funktion ausgedrückt wird. ZB ist der + Operator sehr sinnvoll für alles was irgendwie als eine Form von Zahl durchgehen kann: Vektoren, Matrizen, komplexe Zahlen ... Der * Operator ist bei Vektoren zB schon wieder problematisch ... Skalarprodukt oder Kreuzprodukt?
      Wenn du eine Klasse hast, die funktioniert wie ein Pointer (Smart-Pointer) wär es wohl sinnvoll, die Operatoren -> und * zu überladen. Ein Array-artiger Container würde den [] Operator sinnvoll überladen (siehe std::vector oder auch std::map).



  • hehe danke echt lieb von dir

    in meinem Buch steht folgender Satz:

    Die Operatoren zur dynamischen Speicherverwaltung lassen sich ebenfalls überladen. Dies kann interessant sein, wenn man die Speicherallokation mit der Ausgabe von Debuginformationen verbinden will.

    wie würde das denn aussehen?!



  • Original erstellt von Parapiler:
    hehe danke echt lieb von dir
    in meinem Buch steht folgender Satz:
    Die Operatoren zur dynamischen Speicherverwaltung lassen sich ebenfalls überladen. Dies kann interessant sein, wenn man die Speicherallokation mit der Ausgabe von Debuginformationen verbinden will.
    wie würde das denn aussehen?!

    lustige idee

    void* operator new(size_t s)
    {
       void* p=malloc(s);
       ofstream("memlog.txt",ios::app)>>"new "<<s<<" returns "<<p<<endl;
       return p;
    }
    void operator delete(void* p)
    {
       ofstream("memlog.txt",ios::app)>>"delete "<<p<<endl;
       free(p);
    }
    

    und dann noch ein programm basteln, das das logfile auswertet, ob da ein speicher loch ist oder so.

    ich habe natürlich nicht daran gedacht, zu prüfen, ob ofstream("memlog.txt",ios::app)>>"new "<<s<<" returns "<<p<<endl; voeleicht new aufruft. das gäbe einen interessanten effekt.



  • sorry, ich habe von dem ganzen natürlich wieder mal keinen plan kannst das vielleicht mal erläutern? was willst du prüfen? muss man das was du prüfen willst immer prüfen?!



  • ich habe natürlich nicht daran gedacht, zu prüfen, ob ofstream("memlog.txt",ios::app)>>"new "<<s<<" returns "<<p<<endl; voeleicht new aufruft

    Es darf zumindest new aufrufen. Demzufolge wäre man mit den C-File-Funktionen wohl besser dran.
    Auf der anderen Seite:
    Warum den globalen operator new überladen?
    Prinzipiell sollte es doch auf Klassenebene reichen (optional durch eine mixin-Klasse) und zum Anderen bringen viele Implementationen doch sowieso ein ähnliches (Debug)-new mit.

    PS: Für alle die es nicht gesehen haben sollten, Volkard hat in seinem Beispiel zweimal ausversehen >> statt << geschrieben.



  • wie verbinde ich denn nun die Debuginformationen mit der Speicherreservierung!?



  • und was hat der ofstream da zu suchen?! warum nutzt man den?!



  • Original erstellt von Parapiler:
    sorry, ich habe von dem ganzen natürlich wieder mal keinen plan kannst das vielleicht mal erläutern? was willst du prüfen? muss man das was du prüfen willst immer prüfen?!

    man vergißt gerne mal, ein delete aufzurufen. oder manchmal ruft man eins zu viel auf. mit desem überladenen operator new/delete könnte man sich alle (nicht ganz alle) aufrufe von new/delete mitloggen lassen, und wenn man den verdacht hat. daß was nicht stimmt, das logfile auswerten.
    nur mal so als beispiel, daß du was konkretes in der hand hast auf deine frage "wie würde das denn aussehen?!". man muß das aber nicht so prüfen. da gibts viel bessere sachen.



  • also mal ganz konkret: in einem Programm von mir wird eine Exception ausgelöst und niemand findet einen Fehler, ich dachte, eventuell kann man mittels Exceptionhandling und Operatorüberladung von new den Speicher kontrollieren und so leichter den Fehler finden. Der Fehlerhafte Code ist dieser hier:

    int bytecounter = 0;
            char* recbyte =  NULL;
            unsigned long* bytes = new unsigned long [];
            *bytes = 0;
    
            //warten, bis daten am Socket anliegen
            while (*bytes == 0)
            {
    
                //Anzahl der wartendem Bytes bestimmen und Speicher reservieren
                rc = ioctlsocket (daMember[counter].Socket, FIONREAD, bytes);
    
                if (rc != 0)
                {
                    int n = WSAGetLastError ();
    
                    switch (n)
                    {
                        case WSANOTINITIALISED:
                            cout << "Fehler: Socket ist nicht initialisiert" << endl;
                            return SOCKET_ERROR;
                        case WSAENETDOWN:
                            cout << "Fehler: Das Netzwerksystem hat versagt" << endl;
                            return SOCKET_ERROR;
                        case WSAEINPROGRESS:
                            cout << "Fehler: Ein blockierender Prozess ist im Vorgang" << endl;
                            return SOCKET_ERROR;
                        case WSAENOTSOCK:
                            cout << "Fehler: Der Descriptor ist kein Socket." << endl;
                            return SOCKET_ERROR;
                        case WSAEFAULT:
                            cout << "Fehler: Der Pointer zeigt auf einen ungültigen Adressenbereich" << endl;
                            return SOCKET_ERROR;
    
                    }
    
                }
    
                if (*bytes != 0)
                {
    
                    cout << "Daten liegen vor" << endl;
                    break;
                }
            }
    
            bytecounter = *bytes;
            recbyte = new char [bytecounter];
    
            //Struktur auslesen
            int n = recv (daMember[counter].Socket, recbyte, bytecounter, 0);
    
            if (n == 0)
            {
                cout << "Empfangen fehlgeschlagen" << endl;
                return SOCKET_ERROR;
    
            }
    
            if (n == -1)
            {
                cout << "Empfangen fehlgeschlagen" << endl;
                return SOCKET_ERROR;
            }
    
            cout << recbyte << endl;
    
            //UI- Struktur füllen
            memset (&userinformation, 0, sizeof (UI));
    
            //erstes Byte (BOOLEAN) des Empfangsbuffers in registry kopieren
            memcpy (&userinformation[counter].registry, recbyte + sizeof (int) , sizeof (bool));
    
            //für den Fall eines Gastzuganges
            if (userinformation[counter].registry == false)
            {
    
                //Speicher für den char* reservieren (Nicknamen)
                userinformation[counter].nickname = new char [100];
                //Nicknamen rauskopieren
                memcpy (&userinformation[counter].nickname, recbyte + sizeof (int) + sizeof (bool), bytecounter- sizeof(bool)-sizeof (int));
                //userinformation[counter].nickname = "hallo";
                cout << &userinformation[counter].nickname << endl;
                cout << userinformation[counter].nickname << endl;
                daMember[counter].myuser = new char [bytecounter];
                memcpy (&daMember[counter].myuser, userinformation[counter].nickname, bytecounter - sizeof (bool));
    
            }
    


  • unsigned long* bytes = new unsigned long [];

    bereits hier kann ich nicht folgen.



  • naja dort wird eben nur für einen long Speicher reserviert, ist doch ganz normal:

    ausführlich

    unsigned long bytes* = NULL;
    bytes = new unsigned long [1];



  • Original erstellt von Parapiler:
    also mal ganz konkret: in einem Programm von mir wird eine Exception ausgelöst und niemand findet einen Fehler

    welche exception?



  • beim zweiten cout und dem letzten memcpy wird eine Access violation ausgelöst (letzte und drittletzte zeile) und suche nun nach Hinweisen. Allerdings finde ich keine. Der Speicher ist reserviert, die pointer sind gültig.... ich habe da keine ahnung mehr.... ich dachte eventuell könnte man so ein Abbild von Speicher usw. bekommen und so auf einen Hinweis stoßen, schrittweises durchgehen des Codes ist ja gescheitert (nicht nur bei mir sondern auch bei anderen die ich mal nachgucken haben lasse, niemand hat was gefunden).

    Eventuell etwas zur Vorgeschichte:

    Der Code empfängt einen Buffer von einem Socket. Über das Socket wird eine in einem Buffer serialisierte (mit memcpy hintereinanderkopierte) Struktur. Diese Struktur soll nun wieder entfriemelt werden.



  • Original erstellt von Parapiler:
    beim zweiten cout und dem letzten memcpy wird eine Access violation ausgelöst

    falls du zufällig den msvc benutzt, starte im debug-modus mit f5 und du wirst in den debugger geworfen und dann drück alt+f7 für das fenster mit den bisherigen funktionsaufrufen und schau, ob in den variablen immer das richtige drinsteht und so fängte die ursache einer schutzverletzung normalerweise in wenigen minuten.
    mehr kann ich leider nicht helfen.



  • ja das mache ich schon seit wochen....gibt es kein besseres konzept? Das nützt nämlich alles nicht ich kann damit nix weiter anfangen



  • Original erstellt von Parapiler:
    ja das mache ich schon seit wochen....gibt es kein besseres konzept? Das nützt nämlich alles nicht ich kann damit nix weiter anfangen

    wenns keiner geheimhaltung unterliegt, kann ichs probieren.



  • ne du kannst die Projektdateien gerne haben. Rede mich einfach an, dann schicke ich sie dir....

    AIM: PaRaDoXoNtWeNtY
    ICQ: 157413201
    Yahoo: Parapiler





  • richtig ist

    memcpy (userinformation[counter].nickname, recbyte + sizeof (int) + sizeof (bool), bytecounter- sizeof(bool)-sizeof (int));
    

    willst ja dem memcpy den wert von userinformation[counter].nickname geben, also die adresse den gerade neu angelegten speichers.

    fasch ist

    memcpy (&userinformation[counter].nickname, recbyte + sizeof (int) + sizeof (bool), bytecounter- sizeof(bool)-sizeof (int));
    

    denn du willst den usernamen nicht nehmen und da reinschreiben, wo sich eigentlich die adresse des neuen speichers gemerkt werden soll.



  • Original erstellt von Parapiler:
    schrittweises durchgehen des Codes ist ja gescheitert (nicht nur bei mir sondern auch bei anderen die ich mal nachgucken haben lasse, niemand hat was gefunden).

    durchgehen und sich die variableninhalte angucken.
    also hier recbyte. ins watchfenster hab ich mir gelegt
    recbyte[0]
    recbyte[1]
    recbyte[2]
    recbyte[3]
    recbyte[4]
    recbyte[5]
    recbyte[6]
    und siehe da, in 5 und 6 waren die ersten buchstaben des namens. und dann war ja eigentlich schon die zeile mit dem memcpy als bösewicht identifiziert. nach dem memcpy war nämlich an ziel nix gescheites drin, aer in der quelle stehen ordentliche daten.
    und dann hilt nur noch so lange draufstarren, bis man sich frage, ob das & dort absicht ist.


Anmelden zum Antworten