new überschreiben
-
Ich möchte den operator new überladen, um eine zusätzliche Variable a zu übergeben.
Es soll eine Klasse ZZZ erzeugt werden:struct ZZZ : public zzz {...};Der überladene Operator kennt die Basisklasse zzz, nicht aber ZZZ.
Dies hier funktioniert zwar ....
void* operator new(size_t t, int a) { zzz* z= (zzz*) new BYTE[t]; z->preInit(a); return z; } /// ... main ... ZZZ *z = new(a) ZZZ(xyz); //... delete [] z;.... ist aber untauglich. Vorallem weil in ZZZ keine Destruktoren mehr funktionieren (kein Wunder, ich habe ja auch BYTE's alloziert).
Ich habe innerhalb des new(size_t, int) den Typ ZZZ nicht zur Verfügung. Nur seine Grösse. Deshalb weiss ich nicht, wie ich es besser machen kann.
hgf
-
void zzz::operator new(size_t size, int a) { void *p; if(size != sizeof(zzz)) p = ::operator new(size); else // Deine gewohnte Allozierung }Jetzt noch preInit() virtuell machen.
-
Außerdem solltest du auch noch operator delete passend zu deinem new-Operator überladen, sonst bekommst du Probleme mit der Speicherfreigabe.
(und die Zusammenarbeit zwischen new und delete[] ist schon ohne diese Überladungen problematisch)
@Michael: Virtuelle Funktionen und nicht-initialisierter Speicher? Bist du sicher, daß das gut gehen kann? (ich bezweifle, daß *p eine brauchbare vtable bekommt, wenn es als char[] angelegt wurde)
PS: Ist das wirklich notwendig, a über operator new mitzugeben? Du könntest doch auch einen Ctor mit zwei Parametern füttern, der die notwendigen Arbeiten erledigt.
-
Danke. Funktioniert so ganz gut. Die if/else Abfrage lasse ich lieber weg. Ansonsten wird bei der Erzeugung der Basisklasse zzz der Constructor von zzz zweimal aufgerufen (einmal nach ::new und einmal bei der geschweiften Klammer von zzz::new() ).
void* operator new(size_t size, int a) { void *p; //if(size != sizeof(zzz)) p = ::operator new(size); //else // p = ::new zzz; ((zzz*)p)->preInit(a, 101); return p; }Den Parameter im Konstruktor übergeben, wäre eine Alternative. Aber zumindest weiss ich jetzt besser wie new funktioniert.
Ich habe keine Ahnung, wie es aussehen müsste das delete zu überschreiben. Aber delete funktioniert jetzt glatt ohne Speicherleck. Das delete [] hat sich zusammen mit dem new [] ja erledigt.
Ob virtual preInit() funktionieren würde habe ich noch nicht getestet.
Dass preInit() vor dem Ctor aufgerufen wird, ist vielleicht etwas irregulär, aber für meine Zwecke ok.EDIT:
virtuelles preInit() scheint nicht zu funktionieren: EAccessViolation
-
Die if/else Abfrage lasse ich lieber weg. Ansonsten wird bei der Erzeugung der Basisklasse zzz der Constructor von zzz zweimal aufgerufen (einmal nach ::new und einmal bei der geschweiften Klammer von zzz::new() ).
operator new kümmert sich selber nur um die Speicherverwaltung - da werden überhaupt keine Konstruktoren aufgerufen.
hgf schrieb:
Ich habe keine Ahnung, wie es aussehen müsste das delete zu überschreiben. Aber delete funktioniert jetzt glatt ohne Speicherleck. Das delete [] hat sich zusammen mit dem new [] ja erledigt.
Ganz sicher bin ich auch nicht, aber es müsste in etwa so aussehen:
void operator delete(void*p,int a) { //Aufräumarbeiten delete[] (char*)p; }(p ist der Zeiger, den op new() geliefert hat, a hat die selbe Bedeutung wie beim new)
Ob virtual preInit() funktionieren würde habe ich noch nicht getestet.
Dass preInit() vor dem Ctor aufgerufen wird, ist vielleicht etwas irregulär, aber für meine Zwecke ok.Vor dem Konstruktor-Aufruf kannst du virtuelle Funktionen definitiv nicht einsetzen. Dazu benötigst du schließlich den vptr deiner Klasse und der wird erst im Konstruktor initialisiert (afaik zwischen der Initialisierungsliste und dem Methodenrumpf des Ctor's).
-
CStoll schrieb:
@Michael: Virtuelle Funktionen und nicht-initialisierter Speicher? Bist du sicher, daß das gut gehen kann? (ich bezweifle, daß *p eine brauchbare vtable bekommt, wenn es als char[] angelegt wurde)
Hast Recht, hatte ne ganz vermurkste Vorstellung vom new-Operator

-
Ganz sicher bin ich auch nicht, aber es müsste in etwa so ....
Habe jetzt auch das überladene delete(...) getestet. Wenn man die Operatoren in der Klasse definiert, spielen die Argumente offenbar keine grosse Rolle für die Auswahl des Operators (kein int a als Argument von delete notwendig).
void* zzz::operator new(size_t size, int a) { void *p; p = malloc(size); //p = new char[size]; //p = ::operator new(size); ((zzz*)p)->preInit(a, 101); return p; } void zzz::operator delete(void* p) { free(p); //::delete [] (float*)p; //::delete (void*)p; }Die jeweiligen Pärchen
malloc() - free()
new char[] - delete []
::operator new - deletescheinen alle gleich gut zu funktionieren.
::operator new - delete ist wohl die empfohlene C++ Lösung.Wie der Pointer p im zzz::delete(p) gecastet ist, scheint auch keine Rolle zu spielen. Nur darf er nicht zzz* sein, sonst kommt es zu rekursiven Aufrufen von delete. (Aber float* geht auch, obwohl es keinerlei Sinn macht.)
-
zum direkten Aufruf von "::operator new(s);" sollte eigentlich "::operator delete(p);" gehören, wenn mich nicht alles täuscht. Und das Gecaste dürfte dann auch überflüssig sein, außer du willst eine zu deiner preInit() entsprechende postDestroy()-Methode aufrufen (wobei ich nicht weiß, wofür die notwendig wäre - die entsprechenden Arbeitsschritte passen doch problemlos in den Destruktor).
PS: *brigens muß operator new nicht unbedingt genau "size" Byte anfordern, du kannst auch zusätzlichen Speicher holen (und selber füllen) oder mit new/delete deinen eigenen Speicherpool verwalten.