Typ eines Templates herausfinden



  • hallo,

    ich habe mir einen kleinen Garbage-Collektor gebastelt, der zu Programmende alles,
    was noch nicht vorher wieder freigegeben wurde wieder freigibt.
    dazu habe ich eigene Varianten von new und delete geschrieben.

    beide Funktionen sind als templates definiert.
    mein Problem ist, ich müsste irgendwie herausfinden, ob es sich
    bei dem Typ ob eine Klasse oder etwas anderes handelt,
    denn bei Klassen müsste ich ja den Konstruktor aufrufen...

    kann ich irgendwie herausfinden, ob es sich bei dem Typ um eine Klasse handelt?


  • Mod

    DrakoXP schrieb:

    hallo,

    ich habe mir einen kleinen Garbage-Collektor gebastelt, der zu Programmende alles,
    was noch nicht vorher wieder freigegeben wurde wieder freigibt.

    Das ist nicht besonders sinnvoll, schließlich übernimmt das im Zweifelsfalle das Betriebssystem. Der Zweck eines GC besteht ja darin, Speicher zu recyclen, um ihn während des Programmablaufs wiederzuverwenden.

    dazu habe ich eigene Varianten von new und delete geschrieben.

    beide Funktionen sind als templates definiert.

    Diese Funktionen dürfen Templatefunktionen sein, allerdings bestimmt der Typ des zu konstruierenden Objektes nicht die Signatur der Allokations/Deallokationsfunktion - ein Template an dieser Stelle also wahrscheinlich nicht sinnvoll.

    mein Problem ist, ich müsste irgendwie herausfinden, ob es sich
    bei dem Typ ob eine Klasse oder etwas anderes handelt,

    Das musst du eigentlich nicht

    denn bei Klassen müsste ich ja den Konstruktor aufrufen...

    auf keinen Fall

    kann ich irgendwie herausfinden, ob es sich bei dem Typ um eine Klasse handelt?

    Ja, man kann für einen Typen herausfinden, ob es sich um eine Klasse handelt (zum selbermachen: http://www.c-plusplus.net/forum/viewtopic-var-t-is-194356.html - per Ausschlußverfahren; oder einfach type_traits). Für Allkoationsfunktionen ist das irrelevant, diese bekommen den Typen ohnehin nie zu sehen (new hat nur einen size_t Parameter (und ggf. weitere für placement new, hier kann man Templates einsetzen) und liefert void*, delete bekommt nur void* (und ggf. weitere)).



  • trotzdem ruft new bei Klassen einen Konstruktor auf, gegebenenfalls sogar mit Parameter.
    delete hingegen ruft automatisch bei Klassen den Destruktor auf.

    Das muss ich ebenfalls umsetzen,
    denn meine new und delete nutzen intern malloc() und free()



  • tut er das? New besorgt nur den Speicher. die größte von typ size_t kriegt new übergeben, dh: new int(3); macht new(sizeof(int))->[konstruktor](3)



  • wie bitte soll die Größe von einem Typ in der Lage sein einen Konstruktor aufzurufen?

    sizeof(Mein_Typ)->Mein_Typ(); // ?????



  • new besorgt _nur_ den Speicher, das objekt wird hinterher auf dem allozierten Speicher automatisch konstruiert.



  • nur wie?
    wenn ich malloc und free verwende fehlt mir schließlich dieser Effekt,
    denn diese Funktionen haben ebenjenen Effekt NICHT!



  • DrakoXP schrieb:

    wenn ich malloc und free verwende fehlt mir schließlich dieser Effekt, denn diese Funktionen haben ebenjenen Effekt NICHT!

    Wieso verwendest Du die auch? Verwende halt 'new' und 'delete'.



  • DrakoXP schrieb:

    nur wie?
    wenn ich malloc und free verwende fehlt mir schließlich dieser Effekt,
    denn diese Funktionen haben ebenjenen Effekt NICHT!

    das is ja auch so gedacht. In C gabs noch keine Konstruktoren, wieso sollte sich das verhalten von malloc dahingehend ändern? Mal davon ab ist es auch gut so, denn sonst könnte man sich kaum pufferspeicher besorgen ohne dass direkt objekte erzeugt werden (siehe placement new und die container der stl).



  • Mal langsam - du mußt unterscheiden zwischen den Aufrufen von new/delete im Code und den Funktionen "operator new"/"operator delete". Die Operator-Funktionen holen nur den Speicher, ohne sich um den Inhalt zu kümmern, die Aufrufe im Programm lassen sich Speicher geben und rufen dann den Ctor auf. Und deine überladenen Operatoren werden NIE zu Gesicht bekommen, auf welchen Typ sie losgelassen wurden.

    Eventuell könntest du eine eigene Klassenhierarchie aufbauen und die Speicher-Operatoren in der Basisklasse überladen - dann weißt du bei leerstehenden Objekten zwar nicht exakt, welchen Typ sie haben, aber du weißt, daß sie von deiner Basisklasse abstammen (und den Rest kannst du mit einem virtuellen Dtor aufdröseln lassen).



  • CStoll schrieb:

    Und deine überladenen Operatoren werden NIE zu Gesicht bekommen, auf welchen Typ sie losgelassen wurden.

    Hm, ich glaube inzwischen, dass DrakoXP gar nicht die Funktionen 'operator new' / 'operator delete' überladen hat, sondern die Funktionen 'malloc' und 'free'.

    Da er aber bisher keinen Code gepostet hat, können wir hier natürlich nur raten …



  • Ich hab grad vor einigen Tagen genau zu dem Thema einen kleinen Absatz in Scott Myers' "More effective C++" gelesen, zusammengefasst besagt das etwa:
    Es gibt einmal den Operator new und dann gibt es da noch operator new . Der Unterschied ist folgender:
    Der Operator new ist nicht überladbar und führt immer dazu, dass zwei Schritte ausgeführt werden. Der Operator new ist das new das man aus dem Code kennt, etwa in

    Foo* pf = new Foo();
    

    Die zwei Schritte sind

    1. Aufruf von operator new (hier Foo::operator new , falls überladen) mit sizeof(Foo) als Argument
    2. Aufruf des Konstruktors im erhaltenen Speicher ( Foo::Foo() )

    Die "Funktion" operator new ist überladbar (global und für alle Objekte), hat als Parameter auf jeden Fall ein size_t und wird vom Operator new aufgerufen. Die Aufgabe von operator new ist lediglich, Speicher zu besorgen.

    Soweit ich das verstanden hab ist das oben geschriebene Foo* pf = new Foo(); in etwa mit folgendem Code vergleichbar:

    Foo* pf;
    pf = static_cast<Foo*> Foo::operator new(sizeof(Foo)); //operator new liefert nur Speicher und einen void* darauf
    new(pf) Foo(); //placement new im grade erhaltenen Speicher
    


  • Konrad Rudolph schrieb:

    CStoll schrieb:

    Und deine überladenen Operatoren werden NIE zu Gesicht bekommen, auf welchen Typ sie losgelassen wurden.

    Hm, ich glaube inzwischen, dass DrakoXP gar nicht die Funktionen 'operator new' / 'operator delete' überladen hat, sondern die Funktionen 'malloc' und 'free'.

    Das ist ja noch schlimmer - da ist noch nicht einmal garantiert, daß die ersetzten Funktionen überhaupt verwendet werden (der Default "operator new" darf zwar auf die C Speicherverwaltung zurückgreifen, wenn er will, darf aber auch eine eigene Speicherverwaltung verwenden). Und malloc/free sehen erst recht nicht, was für einen Datentyp du später in den angeforderten Speicherbereich reinpacken wirst.



  • Das von pumuckl sollte das sein was der Autor machen will, aber solgange wir nicht wissen ob DrakoXP den Unterschied zwischen new und operator-new kennt und noch kein Code gesehen haben, ist es schwer ihm irgendwie zu helfen ...



  • Er hat garnix überladen/redefiniert, er hat einfach ein "my_new<T>()" und ein "my_delete<T>()" gemacht.
    Zumindest interpretiere ich das Head-Posting so.



  • genauso wie hustbär sagt, hab ichs geamcht 😉

    template <class T>
    T* mynew(int size)
    {
    void* p = malloc(size * sizeof(T));
    // hier wird noch der Pointer beim GarbageCollector eingetragen...
    return (T*)p;
    }
    
    template <class T>
    void my_delete(T* p)
    {
    // hier wird erstmal der Zeiger aus dem GarbageCollector entfernt...
    free((void*)p);
    }
    

    übrigens: seit wann kümmert sich das System um Speicherlecks ???
    das is mir neu...

    camper schrieb:

    Das ist nicht besonders sinnvoll, schließlich übernimmt das im Zweifelsfalle das Betriebssystem. Der Zweck eines GC besteht ja darin, Speicher zu recyclen, um ihn während des Programmablaufs wiederzuverwenden.



  • DrakoXP schrieb:

    übrigens: seit wann kümmert sich das System um Speicherlecks ???
    das is mir neu...

    camper schrieb:

    Das ist nicht besonders sinnvoll, schließlich übernimmt das im Zweifelsfalle das Betriebssystem. Der Zweck eines GC besteht ja darin, Speicher zu recyclen, um ihn während des Programmablaufs wiederzuverwenden.

    Stimmt auffallend. Jedes moderne Betriebssystem räumt alles, was zu einem Prozess gehört, an dessen Ende weg. Solch ein "Garbage Collector" wie Du ihn implementieren willst ist - wenn überhaupt - dann nur im Debug-Modus sinnvoll, um aufzuzeigen, wo und wann der Speicher allokiert worden ist, der am Programmende nicht freigegeben wurde. Zum Release-Zeitpunkt sollte das Programm solche Lecks dann garnicht mehr enthalten 😉



  • DrakoXP schrieb:

    genauso wie hustbär sagt, hab ichs geamcht 😉

    template <class T>
    T* mynew(int size)
    {
    void* p = malloc(size * sizeof(T));
    // hier wird noch der Pointer beim GarbageCollector eingetragen...
    return (T*)p;
    }
    

    Autsch - damit übergibst du dem Anwender einen Block nicht-initialisierten Speicher und behauptest, daß der ein intaktes Objekt vom Typ T darstellt. Da kannst du schon Wetten darauf abschließen, WANN das umgebende Programm mit einem SegFault ins Nirvana verschwindet.

    übrigens: seit wann kümmert sich das System um Speicherlecks ???
    das is mir neu...

    Schon immer - aber sehr spartanisch. Wenn das Programm beendet wird, holt sich das System allen von dir angeforderten Speicher zurück (allerdings ohne sich um Destruktoren oder ähnliches zu kümmern - zu dem Zeitpunkt existiert dein Speicher nur noch als unstrukturierter Byte-Haufen).



  • In deiner new-Methode kannst du placement-new benutzen um den Konstruktor von T aufzurufen (dafür ist placement-new da) und den Destruktor kann man ja explizit aufrufen.
    Dann musst du nur noch zwischen built-ins und Objekten unterscheiden und das geht (wie bereits gesagt) ganz gut mit type_traits (kannst z.B. boost::type_traits benutzen).



  • @lolz: Dann werden die Dinger aber immer default-konstruiert, es sei denn, man erstellt für eine arbiträre Anzahl an Parametern jeweils unterschiedliche Überladungen der Funktionen.

    Da ist das Überladen von 'operator new' und 'operator delete' doch einfacher. 😉


Anmelden zum Antworten