PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Destruktoren


WhiteVelvet
2006-09-02, 13:49:33
Ich habe gerade ein kleines Verständnisproblem mit Destruktoren von Klassen. Ich habe folgenden Code:

Bitmap::Bitmap()
{
// Erzeuge 2D Array
chartable = new int*[128];
for (int i=0; i<128; i++)
chartable[i] = new int[56];

// Initialisiere Array
for (int i=0; i<128; i++)
for (int j=0; j<56; j++)
chartable[i][j]=0;

// Fülle Array mit Zeichen/Daten
CreateCharTable();
}

Bitmap::~Bitmap()
{
for (int i=0; i<128; i++)
delete chartable[i];
delete chartable;
AfxMessageBox("q");
}

Der Klassenkonstruktor legt einen 2D-Array an. Der muss beim Programmende wieder gelöscht werden, richtig? Also schreibe ich das delete in den Destruktor. Um zu gucken, ob der da überhaupt durchrennt, steht da dieses AfxMessageBox. Das kommt aber niemals. Läuft er nicht in den Destruktor? Mach ich was falsch? Ich könnte hinter dem Beenden-Botton natürlich den Destruktor explizit durch myBitmap->~Bitmap() aufrufen, aber das ist nicht gewollt, oder doch?

Gast
2006-09-02, 13:56:34
Wenn eine WM_QUIT Nachricht in der Nachrichtenschlange ist wird die Message Box sofort wieder geschlossen. Warum nimmst du nicht den Debugger?

WhiteVelvet
2006-09-02, 13:58:25
Ja stimmt eigentlich :D Ok, er läuft doch durch ;)

Gast
2006-09-02, 13:59:51
es muss delete[] heißen.

Trap
2006-09-02, 14:06:52
Das erste Problem ist, dass du Speicher den du mit new[] angelegt hast mit delete löschst. Das muss immer zusammen passen, mit new[] anlegen => mit delete[] löschen, mit new anlegen => mit delete löschen.

WhiteVelvet
2006-09-02, 14:07:44
Ok, nächstes Problem: Das delete erzeugt eine Exception, obwohl das Objekt vorhanden ist:

tabGraphView::~tabGraphView()
{
if (m_oglGraph)
delete m_oglGraph; <-- Exception
}

BOOL tabGraphView::OnInitDialog()
{
// Instanz von COpenGLGraph erzeugen
m_oglGraph = new COpenGLGraph(); <-- hier wirds erzeugt
}

Der Destruktor von COpenGLCtrl ist leer, aber vorhanden. Könnte der Fehler von dort kommen?

Trap
2006-09-02, 14:17:41
Das if kann man sich sparen, delete auf 0-pointern ist erlaubt und macht nichts.

Bei deinem Code fehlen die Typen (für Leser hier im Forum, nicht für den Compiler).

Wenn delete Exceptions wirft hat man vorher meistens irgendwas im Heap überschrieben weil ein Pointer irgendwo hingelaufen ist wo er nicht hingehört.

WhiteVelvet
2006-09-02, 14:19:37
Das if kann man sich sparen, delete auf 0-pointern ist erlaubt und macht nichts.

Bei deinem Code fehlen die Typen.

Wenn delete Exceptions wirft hat man vorher meistens irgendwas im Heap überschrieben weil ein Pointer irgendwo hingelaufen ist wo er nicht hingehört.

Der Typ steht woanders. Ich schreibe erstmal alle deletes in alle Klassen, vielleicht mag er es nicht, wenn ich nur 2 von 10 Klassen mit Destruktoren ausgestattet hab.

WhiteVelvet
2006-09-02, 14:44:01
Wunderbar, jetzt ist die Exception wieder weg :) Muss ich sonst nochwas "zerstören" ausser mit new erstellte Objekte/Arrays?

WhiteVelvet
2006-09-02, 17:38:16
... und wieder da... argh... wie kann ich am besten herausfinden, wo genau dieser Heap-Fehler ist? Und was noch seltsamer ist: Das passiert nur um Debug-Modus, aber nicht im Release-Modus, da kommt keine Exception.

Expandable
2006-09-02, 18:51:57
Im Debug-Modus wird AFAIK (zumindest wenn du VS verwendest) Debug-Code (ach nee ;-) ) "eingeschleust", um solche Fehler zu entdecken. Der Code wird im Release-Mode aus Performancegründen rausgeworfen.

Such doch mal die Stelle, wo genau die Exception geworfen wird. Direkt bei delete m_oglGraph oder im Destruktor von m_oglGraph (mit dem Debugger und F11 durchsteppen).

Wie genau lautet denn die Exception?

WhiteVelvet
2006-09-03, 10:49:38
Juhu, dank der Recherche für Deine Frage habe ich den Fehler gefunden. 3 Arrays werden erst später im Programm mit new erzeugt. Wenn ich starte und direkt beende sind die nur definiert gewesen und dann kommt das delete nicht so gut ;)

Expandable
2006-09-03, 14:15:03
Also wenn ich das richtig verstehe, hast Du dann nur einen kleinen Schönheitsfehler gemacht: Pointer initialisiert man IMMER auf 0. Somit gibt's damit niemals Probleme. Also Beispiel:

int* myArray = 0;
... weiter im Code... <- hier kann bereits gefahrlos ein delete[] myArray kommen
myArray = new int[myArraySize];
... weiter im Code...
delete[] myArray;

Was Du eben nicht machen solltest:
int* myArray; <- zeigt auf irgendwas
... weiter im Code, kommt hier ein delete[], hast Du ein Problem
myArray = new int[myArraySize];
...
delete[] myArray;

Falls die Arrays zu einer Klasse gehörten, direkt im Konstruktor sowas machen:
class MyClass
{
private:
int* myArray;
public:
MyClass() : myArray(0) {}
~MyClass() { delete[] myArray; }
};

... oder wie Du Deinen Konstruktor halt brauchst. Somit ist garantiert, dass der Destruktor ohne Fehlermeldung durchläuft. Falls irgendeine andere Methode delete[] myArray aufruft, den Pointer danach UNBEDINGT wieder auf 0 setzen -> sonst wieder Probleme mit dem Destruktor.

Trap
2006-09-03, 14:29:49
Ich halte manuelles Management von Pointern in den allermeisten Fällen für völlig unnötige Komplexität. Besonders sobald man 2 oder mehr Pointer in einem Geltungsbereich hat.

std::auto_ptr, boost::shared_ptr, std::vector, std::list und std::string reichen in 95% um das manulle Management loszuwerden.

zeckensack
2006-09-03, 15:22:55
Ich empfehle Präambeln ...class
Horst
{
public:
Horst():schnrz(0) { /* Hier könnte ihr ctor-Code stehen */ }
private:
void* schnrz;
};