Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Newbie-Question: Ueberladen des delete Operator

35 views
Skip to first unread message

franz....@gmx.net

unread,
Feb 27, 2012, 11:35:54 AM2/27/12
to
Hallo NG,

ich bin ein totaler Anfänger in Sachen C++ und stehe nun vor einem für
mich nicht nachvollziehbaren Problem:

Bei einer Applikation verwende ich Klassen welche mit new und delete
erstellt bzw. wieder entfernt werden. Da ich mich nun ein wenig mit
Speicherallozierung beschäftigen möchte habe ich die Operatoren new
und delete überschrieben und durch eigene "Versionen" ersetzt.

Zwecks Debugging lasse ich mir auch immer die jeweiligen
Speicheradressen anzeigen damit ich sehe wo new etwas alloziert hat
bzw. wo delete etwas entfernt.

Bei selber geschriebenen Klassen funktioniert es auch wunderbar. Ich
alloziere ein Objekt mittels new und wenn dieses später mit delete
wieder freigegeben wird sehe ich auch die identischen Speicheradresse
des Objekts.

Verwende ich dagegen eine Klasse aus einem Framework (In meinem Falle
ein wxWindow* aus wxWidgets) so erhalte ich beim Aufruf von delete
eine komplett andere Speicheradresse als ich eigentlich beim
vorherigen Aufruf mittels new erhalten hatte.

Kann mir da bitte jemand einen Tipp geben wo ich hier ansetzen kann?
Hintergrund ist das ich auch "Speicherlöcher" mit den überschriebenen
Operatoren finden möchte. Das ist aber ein Problem wenn meine Version
von delete andere Pointer verwendet als der ursprüngliche Aufruf von
new.

Vielen Dank!
Franz

Daniel Krügler

unread,
Feb 27, 2012, 2:08:57 PM2/27/12
to
On 27 Feb., 17:35, franz.mau...@gmx.net wrote:
> ich bin ein totaler Anfänger in Sachen C++ und stehe nun vor einem für
> mich nicht nachvollziehbaren Problem:
>
> Bei einer Applikation verwende ich Klassen welche mit new und delete
> erstellt bzw. wieder entfernt werden. Da ich mich nun ein wenig mit
> Speicherallozierung beschäftigen möchte habe ich die Operatoren new
> und delete überschrieben und durch eigene "Versionen" ersetzt.

Meine Vermutung ist hier, dass du diese Operatoren im globalen
Namenraum definiert hast, richtig? (Das ist keine Fehlervermutung,
sondern bloss eine Klärungsfrage, um offensichtliche Fehler
auszuschliessen).

Wenn du das machst, denke bitte daran, dass du *alle* acht ersetzbaren
operatoren ersetzt - auch die für Arrays. Das sind also die Formen (in
C++03-Schreibweise):

void* operator new(std::size_t size);
void operator delete(void* ptr) throw();
void* operator new(std::size_t size, const std::nothrow_t&) throw();
void operator delete(void* ptr, const std::nothrow_t&) throw();

void* operator new[](std::size_t size);
void* operator new[](std::size_t size, const std::nothrow_t&) throw();
void operator delete[](void* ptr) throw();
void operator delete[](void* ptr, const std::nothrow_t&) throw();

Du musst die nicht alle neu schreiben. Technisch reicht es, wenn du

void* operator new(std::size_t size);
void operator delete(void* ptr) throw();

definierest und die anderen diese aufrufen lässt (Bedenke aber, dass
die std::nothrow_t-Überladungen niemals eine Exception werfen, d.h.
also intern also z.B. einen try-catch-Block um den Aufruf von
operator new und beim Exception-Fall einen Null-Pointer zurückgeben.

Leider ist erst im neuen C++11-Standard geklärt wurden, dass ein
Benutzer nur die eben genannten zwei Operatoren definieren muss,
um damit den Effekt zu erzielen, dass die anderen 6 schon gleich
diese zwei intern aufrufen.

> Zwecks Debugging lasse ich mir auch immer die jeweiligen
> Speicheradressen anzeigen damit ich sehe wo new etwas alloziert hat
> bzw. wo delete etwas entfernt.

Ja, das ist ein recht häufiger Anwendungsfall dafür.

> Bei selber geschriebenen Klassen funktioniert es auch wunderbar. Ich
> alloziere ein Objekt mittels new und wenn dieses später mit delete
> wieder freigegeben wird sehe ich auch die identischen Speicheradresse
> des Objekts.

An dieser Stelle würde ich dich gerne bitten, die *genaue* Syntax
hier wiederzugegeben, wie du new und delete bei dir im Programm-Code
aufrufst. Erklärung siehe unten.

> Verwende ich dagegen eine Klasse aus einem Framework (In meinem Falle
> ein wxWindow* aus wxWidgets) so erhalte ich beim Aufruf von delete
> eine komplett andere Speicheradresse als ich eigentlich beim
> vorherigen Aufruf mittels new erhalten hatte.

Dafür kann es mehrere Ursachen geben. Zum einen ist wichtig, *wie* du
new bzw. delete aufrufst. Zum anderen solltest du wissen, dass die
wxObject-Basisklasse beim Setzen des Flags __WXDEBUG__ selber
klassenspezifische Operatoren für new und delete (aber nicht für
die Array-Formen, was meiner Meinung nach ein Designfehler ist)
bereitstellt. Hast du das Flag gesetzt?
Zudem haben die Defines wxUSE_GLOBAL_MEMORY_OPERATORS
und wxUSE_DEBUG_NEW_ALWAYS auf new-Operatoren. Hast du die
gesetzt und wenn ja: wie?

Andere Möglichkeiten sind, dass du gegen unterschiedliche
Laufzeit-Bibliotheken kompilierst (Debug/Release oder
statisch/dynamisch z. B.). Welchen Compiler und welche
Laufzeitbibliotheken verwendest du?

Hier findest du ein paar Informationen zu den
Konfigurationsmöglichkeiten
von wxWidgets:

http://wiki.wxwidgets.org/MSVC_Setup_Guide

Noch eine beliebter Fehler ist die Allokation mit new[] und dem
anschliessenden Löschen mit delete (statt mit delete[]) oder
umgekehrt (Daher obige Frage nach deiner genauen Aufrufsform).

> Kann mir da bitte jemand einen Tipp geben wo ich hier ansetzen kann?
> Hintergrund ist das ich auch "Speicherlöcher" mit den überschriebenen
> Operatoren finden möchte. Das ist aber ein Problem wenn meine Version
> von delete andere Pointer verwendet als der ursprüngliche Aufruf von
> new.

Leider habe ich nur ein paar oben erwähnte Tipps parat, habe bisher
nur am Rande mit wxWidgets zu tun gehabt.

Viel Erfolg bei der Fehlersuche und besten Gruss aus Bremen,

Daniel Krügler

Franz Maurer

unread,
Feb 29, 2012, 1:50:40 AM2/29/12
to
>> Bei einer Applikation verwende ich Klassen welche mit new und delete
>> erstellt bzw. wieder entfernt werden. Da ich mich nun ein wenig mit
>> Speicherallozierung beschäftigen möchte habe ich die Operatoren new
>> und delete überschrieben und durch eigene "Versionen" ersetzt.
>
>Meine Vermutung ist hier, dass du diese Operatoren im globalen
>Namenraum definiert hast, richtig? (Das ist keine Fehlervermutung,
>sondern bloss eine Klärungsfrage, um offensichtliche Fehler
>auszuschliessen).

Das ist richtig, ich habe die Operatoren im globalen Namensraum
definiert.

>> Bei selber geschriebenen Klassen funktioniert es auch wunderbar. Ich
>> alloziere ein Objekt mittels new und wenn dieses später mit delete
>> wieder freigegeben wird sehe ich auch die identischen Speicheradresse
>> des Objekts.
>
>An dieser Stelle würde ich dich gerne bitten, die *genaue* Syntax
>hier wiederzugegeben, wie du new und delete bei dir im Programm-Code
>aufrufst. Erklärung siehe unten.

Gerne. LogFrame ist eine eigene Klasse welche von wxFrame abgeleitet
ist. mLog ist ein Pointer darauf:

// Beim Initialisieren
mLog = new LogFrame(this, _("Log"),
wxPoint(0, 0),
wxSize(640, 200));
cout << "ALLOC: " << mLog << endl;

// Beim Entfernen
if (mLog) delete mLog;

Die delete Operatoren habe ich wie folgt überschrieben (Die
tatsächliche Speicherfreigabe ist hier noch nicht drin, rein nur zum
Debuggen!):

#include <new>
void operator delete(void *block) throw()
{
printf("DEBUG: %p\n", block);
}
void operator delete[](void *block) throw()
{
printf("DEBUG: %p\n", block);
}

Bei der Ausgabe von "ALLOC:" erhalte ich eine andere Ausgabe als beim
"DEBUG: " der delete-Operatoren obwohl der gleiche Pointer verwendet
wird.

Bei externen Tools wie http://www.neoegm.com/software/cppmemdbg/
bekomme ich das gleiche Ergebnis (Pointer unterschiedlich) Wie
geschrieben funktioniert es bei eigenen Klassen (welche nicht von
wxWindows/wxFrame abgeleitet sind) - Nur eben in Verbindung mit
wxWidgets bekomme ich unterschiedliche Werte.

>Dafür kann es mehrere Ursachen geben. Zum einen ist wichtig, *wie* du
>new bzw. delete aufrufst. Zum anderen solltest du wissen, dass die
>wxObject-Basisklasse beim Setzen des Flags __WXDEBUG__ selber
>klassenspezifische Operatoren für new und delete (aber nicht für
>die Array-Formen, was meiner Meinung nach ein Designfehler ist)
>bereitstellt. Hast du das Flag gesetzt?
>Zudem haben die Defines wxUSE_GLOBAL_MEMORY_OPERATORS
>und wxUSE_DEBUG_NEW_ALWAYS auf new-Operatoren. Hast du die
>gesetzt und wenn ja: wie?

__WXDEBUG__, wxUSE_GLOBAL_MEMORY_OPERATORS und wxUSE_DEBUG_NEW_ALWAYS
habe ich nicht gesetzt.
>Andere Möglichkeiten sind, dass du gegen unterschiedliche
>Laufzeit-Bibliotheken kompilierst (Debug/Release oder
>statisch/dynamisch z. B.). Welchen Compiler und welche
>Laufzeitbibliotheken verwendest du?

Ich verwende MinGW und Eclipse als GUI. Der Compiler hat folgenden
Version-String: g++ (tdm-1) 4.5.2
wxWidgets läuft in der Version wxWidgets-2.8.12

Kompiliert wird alles statisch da ich keine Abhängigkeiten möchte und
das größere Binary in Kauf nehme.

>
>Viel Erfolg bei der Fehlersuche und besten Gruss aus Bremen,
>
>Daniel Krügler

Vielen Dank für Deine Infos! Ich werde bei den von Dir erwähnten
DEFINES ansetzen da ich hier doch ein wenig unsicher bin und ich
vermute das wxWidgets da im Hintergrund etwas ändert von dem ich jetzt
noch nichts weiß.

Liebe Grüße
Franz

Björn

unread,
Mar 14, 2012, 2:21:32 PM3/14/12
to

Hi Franz,

Franz Maurer wrote:
> Gerne. LogFrame ist eine eigene Klasse welche von wxFrame abgeleitet
> ist. mLog ist ein Pointer darauf:
>
> // Beim Initialisieren
> mLog = new LogFrame(this, _("Log"),
> wxPoint(0, 0),
> wxSize(640, 200));
> cout << "ALLOC: " << mLog << endl;
>
> // Beim Entfernen
> if (mLog) delete mLog;

Kleine Nebenbemerkung: Der C++-Standard schreibt vor, dass delete auf einen
Nullpointer keinen Effekt haben darf. Ich würde dir empfehlen, das bei
deiner Überladung auch sicher zu stellen.

> Die delete Operatoren habe ich wie folgt überschrieben (Die
> tatsächliche Speicherfreigabe ist hier noch nicht drin, rein nur zum
> Debuggen!):
>
> #include <new>
> void operator delete(void *block) throw()
> {
> printf("DEBUG: %p\n", block);
> }
> void operator delete[](void *block) throw()
> {
> printf("DEBUG: %p\n", block);
> }
>
> Bei der Ausgabe von "ALLOC:" erhalte ich eine andere Ausgabe als beim
> "DEBUG: " der delete-Operatoren obwohl der gleiche Pointer verwendet
> wird.

Hier fällt mir auf, dass du bei new den Pointer mit cout ausgibst und bei
delete mit printf. Bei printf schreibst du dem Compiler vor, dass er die
übergebene Variable als Pointer zu interpretieren und entsprechend
auszugeben hat. Bei cout überlässt du dem Compiler die Interpretation. Es
könnte nun sein, dass deine wx-Bibliothek eine Überladung von operator<<()
enthält, die dafür sorgt, dass cout im Falle eines Pointers auf einen
LogFrame nicht den Pointer selbst, sondern irgendetwas anderes ausgibt. Für
deine eigenen Klassen hast du vermutlich keine solche Überladung
implementiert.

Ich würde dir empfehlen beide Ausgaben gleich zu machen und auch beim new
den Pointer vor der Ausgabe explizit in ein void* umzuwandeln. Beim delete
wird das automatisch bei der Übergabe gemacht. Also z.B. (ungetestet):

mLog = new LogFrame(this, _("Log"),
wxPoint(0, 0),
wxSize(640, 200));
printf("ALLOC: %p\n", static_cast<void *>(mLog));


Gruß
Björn
0 new messages