globale new operator überladen



  • Hallo zusammen!

    Ich möchte den globalen new und delete operator überladen, um so memoryleaks ausfindig machen zu können. Leider will der Compiler meinen Code nicht und ich weiß auch mit der Fehlermeldung jetzt nicht wirklich was anzufangen. Der Code sieht wie folgt aus:

    #ifdef _DEBUG
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #define DEBUG_NEW new( THIS_FILE, __LINE__)
    #else
    #define DEBUG_NEW new
    #endif
    #define new DEBUG_NEW
    
    #ifdef _DEBUG
    
    inline void * __cdecl operator new(unsigned int size,
                                       const char *file, int line)
    {
    	void *ptr = (void *)malloc(size);
    	AddTrack((DWORD)ptr, size, file, line);
    	return(ptr);
    };
    inline void __cdecl operator delete(void *p)
    {
    	RemoveTrack((DWORD)p);
    	free(p);
    };
    
    #endif
    

    und der Compiler (VC++ 6.0) sagt dazu folgendes:

    error C2061: Syntaxfehler : Bezeichner 'THIS_FILE'
    

    Die Fehlermeldung bezieht sich auf die Zeile mit der Deklaration des neuen new-Operators. Habt jemand eine Ahnung, wo das Problem liegt? Oder gibt es eine Möglichkeit sich die Präprozessorausgabe anzusahen?

    Danke!
    Karsten


  • Mod

    die #defines sind im weg

    inline void * __cdecl operator new(unsigned int size,
                                       const char *file, int line)
    

    wird zu

    inline void * __cdecl operator new( THIS_FILE, __LINE__)(unsigned int size,
                                       const char *file, int line)
    


  • ist jetzt klar, danke! hab die defines hinter die declaration verschoben und etwas geändert. sieht jetzt so aus:

    #include <string>
    
    using namespace std;
    
    #ifdef _DEBUG
    
    #include <windows.h>
    #include <list>
    #include <string>
    
    void AddTrack(DWORD addr,  DWORD asize,  const char *fname, DWORD lnum);
    
    void RemoveTrack(DWORD addr);
    
    void DumpUnfreed();
    
    extern int counter;
    
    inline void * __cdecl operator new(unsigned int size,
                                       const char *file, int line, int dummy)
    {
    	char str[2048];
    	sprintf( str, "allocated: %d : %d BYTES at %s (%d)\r\n", ++counter, size, file, line );
    
    	OutputDebugString( str );
    
    	void *ptr = (void *)malloc(size);
    	AddTrack((DWORD)ptr, size, file, line);
    	return(ptr);
    };
    
    inline void __cdecl operator delete(void *p)
    {
    	RemoveTrack((DWORD)p);
    	free(p);
    };
    
    #undef DEBUG_NEW
    #define DEBUG_NEW new( __FILE__, __LINE__, 1)
    #else
    #define DEBUG_NEW new
    #endif
    #define new DEBUG_NEW
    


  • Und was machst du wenn du mal die Funktion operator new für eine Klasse überladen willst? 🙂



  • Sehr schlechter Stil, bennen das Define um in etwas anderes als new.
    Und btw. vc bietet so ein tracking system von Haus aus an.



  • @David_pb: falls du meinst, was ich mache wenn ich den new-Operator einer bestimmten Klasse überladen will, dann ist das nicht nötig! Alle new-Operatoren rufen irgendwann den globalen Operator auf den ich da überschreibe.

    @lolz: ich geb dir auf jeden Fall recht, dass der Stil nicht wirklich gut ist. Das ganze soll auch nur zur schnellen Fehlerbehebung dienen. Leider kann ich die von VC bereitgestellten Trackingfunktionen nicht benutzen, da ich an einem reinen plattfromunabhängigen C++-Projekt arbeite und VC lediglich die Entwicklungsumgebung unter Windows ist. Also kein AFX-irgendwas. Kleine Frage noch: wie würdest du die defines nennen? Ich hab mal in den VC-headern geguckt und die machen das fast genau so.



  • Anzumerken ist noch, dass im Debug so auch kein placement new mehr möglich ist.


  • Mod

    Nur als Idee, die mir gerade in den Sinn kommt: Diese Art von Protokollierung könnte man auch außerhalb des Operators durchführen und hat dann insoweit keine Schwierigkeiten mit placement new. Im Gegenzug werden einige andere Konstrukte damit fehlerhaft. Nähmlich solche, die unmittelbar sizeof ++,––,~,!,–,+,&,* oder .* , –>* auf das Ergebnis von new anwenden (wobei das Problem für die erste Gruppe durch ein paar Tricks gelöst werden kann - möglicherweise abgesehen von sizeof)

    struct NewLogger
    {
        NewLogger(const char* file, const char* Line) : file_( file ), line_( line ) {}
        const char* file_;
        const char* line_;
        template<typename T>
        T* operator%(T* p)
        {
            // Logging
            return p;
        }
    };
    #define new NewLogger(__FILE__,__LINE__)%new
    


  • Danke für die Idee mit dem NewLogger! Auch ein interessanter Ansatz.

    da muss ich doch glatt mal ne dumme Frage stellen: was ist ein placement new? Entweder ist mir das noch nie so untergekommen, oder ich kenne es nicht. Würde mich über aufklärung freuen. Also was ist das und wann setzt man dies sinnvoller Weise ein?

    Ausserdem hab ich noch ein Problem. Mein Logger funktioniert soweit, aber scheint nicht alles mitzubekommen, denn er spuckt weniger aus, als das was mir VC-Studio 6 anzeigt. Dummerweise bekomme ich von VC keine Informationen, wo der Speicher allociert wird. Hat jemand eine Idee, was man noch machen könnte, neben einem Logger?



  • Placement new bezeichnet die new - Form die wahrscheinlich so definiert ist:

    void* operator new(size_t, void *mem)
    {
       return mem;
    }
    

    Aufgerufen wird sie so:

    new (buffer) T;
    

    Ich meine alle überladen Versionen von new die einen Parameter mehr haben werden als placement new bezeichnet und die obige Version ist sogar in <new> mit dabei und man nennt diese den "placment new"...

    Stell dir vor du hast bereits rohen Speicher bzw am Anfang deines Programmes zur Sicherheit einen Speicher-Block besorgt. Später willst du dann in diesen Speicherbereich dein Objekt Konstruieren und das geht dann mit placement new.

    Wie du siehst tut placment new nichts anderes als den Speicher zurückzugeben.
    Das reicht auch völlig aus den wenn new den operator new aufruft tut er ja dies um Speicher zu bekommen und dann reicht es ja aus deinen Speicher einfach wieder zurückzugeben.

    Du kannst dir deine eigenen placement new's konstruieren die zB auch so aussehen.

    void* operator new(size_t size, ostream& stream)
    {
      stream << ......
      return ::operator new(size); // Einfach globalen op aufrufen ( hoffe das war so richtig :) )
    }
    //...
    new (cout) T;
    

    So kannst du dir dann alles direkt protokolieren.
    Danach musst du aber auch placment delete benutzen da der Speicher ja nicht von newm, der sieht dann genau so aus mit einem Parameter mehr.

    Vergiss nicht zu all deinen placement new's dann auch die richtigen placement delete's zu schreiben.


  • Mod

    littlejohn schrieb:

    da muss ich doch glatt mal ne dumme Frage stellen: was ist ein placement new? Entweder ist mir das noch nie so untergekommen, oder ich kenne es nicht. Würde mich über aufklärung freuen. Also was ist das und wann setzt man dies sinnvoller Weise ein?

    Zum Beispiel das hier:

    new( __FILE__, __LINE__, 1) T;
    

    Jeder new Ausruck, bei dem zusätzliche Argumente (in den runden Klammern) an den new-operator geliefert werden, benutzt placement-new. Die Bezeichnung ist Folge der typischen Benutzung: man möchte ein Objekt an einer bestimmten Stelle konstruieren, ohne extra Speicher anzufordern (d.h. man hat den Speicher bereits vorher angefordert). Das ist nützlich und notwendig zum Beispiel für Containerklassen. In diesem Standardfall des placement new (der als einziger auch tatsächlich von vornherein existiert) wird als zusätzliches Argument ein entsprechneder Pointer übergeben. zum Beispiel:

    struct Foo {};
    int main()
    {
        Foo foo;          // legt ein Foo an, aber keine Initialisierung, da POD
        new(&foo) Foo();  // legt ein initialisiertes Foo an
    };
    

    in diesem Falle nat. nicht sehr nützlich. Bei Placement new wird ein entsprechend überladener new-operator aufgerufen, der die zusätzlichen Argumente als 2.,3. usw. Argument erhält. Interessant ist dabei, dass es kein placement-delete gibt (wohl aber plcement-delete operatoren, die aber nur bei exceptions während eines placement news oder eben explicit aufgerufen werden), im Normalfall will man ja das Objekt nur zerstören und benutzt dafür einen expliziten Destruktoraufruf.



  • Habe nochmal aufgrund camper's Post nachgeblätter und gesehen das diese Aussage falsch war:

    Freak_Coder schrieb:

    Danach musst du aber auch placment delete benutzen da der Speicher ja nicht von newm, der sieht dann genau so aus mit einem Parameter mehr.

    Wie camper es sagt gibts aber ein placment delete das bei nicht erfolgreicher Speichernaforderung aufgerufen wird.

    Dein placement delete musst die selben Parameter haben wie dein p-new, damit diese im Fehlerfall aufgerufen werden können.

    Deine Objekt musst du dann selbst destruieren und danach deinen Speicher so freigeben wie du ihn auch besorgt hast.



  • es geht auch folgendes:

    #include <cstdlib>                                                                                                                                                       
    #include <iostream>                                                                                                                                                      
    using namespace std;                                                                                                                                                     
    
    #if DEBUG                                                                                                                                                                         
    void* operator new(size_t n) {                                                                                                                                           
        cout << "new of " << n << endl;                                                                                                                                      
        void*d = malloc(n);                                                                                                                                                  
        return d;                                                                                                                                                            
    }                                                                                                                                                                        
    void operator delete(void * p) {                                                                                                                                         
        cout << "Deleting object " << p << endl;                                                                                                                             
        free(p);                                                                                                                                                             
    }                                                                                                                                                                        
    void operator delete[](void * p) {                                                                                                                                       
        cout << "Deleting array" << p << endl;                                                                                                                               
        free(p);                                                                                                                                                             
    }                                                                                                                                                                        
    
    #endif                                                                                                                                                                   
    main() {                                                                                                                                                                 
        char* x = new char[10];                                                                                                                                              
    
        delete[] x;                                                                                                                                                          
    }
    

    man darf new und delete durch eigenes ersetzen


  • Mod

    r0nny schrieb:

    es geht auch folgendes:

    man darf new und delete durch eigenes ersetzen

    Das stand nicht zur Debatte. Das einfache Überladen ist für unsere Zwecke aber nutzlos, weil wir zusätzliche Daten, die nur der Aufrufer kennt, benötigen.



  • littlejohn schrieb:

    @David_pb: falls du meinst, was ich mache wenn ich den new-Operator einer bestimmten Klasse überladen will, dann ist das nicht nötig! Alle new-Operatoren rufen irgendwann den globalen Operator auf den ich da überschreibe.

    Sehr schlau ausgedacht. Es mag aber sein das du die Art der Speicherreservierung anders handhaben willst als bei deiner globalen Funktion operator new.

    grüße


Anmelden zum Antworten