2009-10-26 2 views
1

Je reçois une mauvaise erreur. Quand j'appelle supprimer sur un objet en haut d'une hiérarchie d'objets (en espérant à la cause de la suppression de ses objets enfants), mon progamme quitte et je reçois ceci:Questions sur l'allocation de mémoire C et supprimer

*** glibc detected *** /home/mossen/workspace/abbot/Debug/abbot: double free or corruption (out): 0xb7ec2158 ***

suivi de ce qui ressemble à une décharge de la mémoire de quelques sortes. J'ai recherché cette erreur et de ce que je comprends, il semble se produire lorsque vous essayez de supprimer la mémoire qui a déjà été supprimé. Impossible car il n'y a qu'un seul endroit dans mon code qui tente cette suppression. Voici la partie farfelue: elle ne se produit pas en mode débogage. Le code en question:


Terrain::~Terrain() 
{ 
    if (heightmap != NULL) // 'heightmap' is a Heightmap* 
    { 
     cout << "heightmap& == " << heightmap << endl; 
     delete heightmap; 
    } 
} 

J'ai commenté tout le destructor heightmap, et encore cette erreur. Lorsque l'erreur se produit,

heightmap& == 0xb7ec2158

est imprimé. En mode débogage, je peux parcourir le code lentement et

heightmap& == 0x00000000

est imprimé, et il n'y a pas d'erreur. Si je commente le 'delete heightmap'; ligne, l'erreur ne se produit jamais. Le destructeur ci-dessus est appelé à partir d'un autre destructeur (classes séparées, pas de destructeurs virtuels ou quelque chose comme ça). Le pointeur heightmap est new'd dans une méthode comme ceci:


Heightmap* HeightmapLoader::load() // a static method 
{ 
    // .... 
    Heightmap* heightmap = new Heightmap(); 
    // ....other code 
    return heightmap; 
} 

Se pourrait-il quelque chose à voir avec le retour d'un pointeur qui a été initialisé dans l'espace de la pile d'une méthode statique? Est-ce que je fais la suppression correctement? D'autres conseils sur ce que je pourrais vérifier ou faire mieux?

Répondre

2

Qu'advient-il si load() est jamais appelé? Est-ce que votre constructeur de classe initialise heightmap, ou est-il non initialisé quand il arrive au destructeur?

, vous dites aussi:

... supprimer la mémoire qui a déjà été supprimé. Impossible car il n'y a qu'un seul endroit dans mon code qui tente cette suppression.

Cependant, vous n'avez pas pris en considération le fait que votre destructor pourrait être appelé plus d'une fois lors de l'exécution de votre programme.

+0

Oui, c'était ça. Je pensais que heightmap * était en train d'être défini (pas dans le constructeur, mais dans une méthode setter), hélas ce n'était pas le cas. Merci, Greg! – Mossen

+1

Toujours initialiser tous vos pointeurs pendant la construction. Un pointeur nul peut être supprimé en toute sécurité (no-op). Si votre constructeur a placé le pointeur sur 0 dans la liste d'initialisation, le code sera correct dès le début. –

+1

Et bien sûr il pourrait y avoir une construction de copie et/ou une assignation qui fait pointer deux objets vers la même zone mémoire ... –

1

Il est tout à fait possible que vous appelez que dtor deux fois; en mode débogage, le pointeur est mis à zéro lors de la suppression, en mode optimisé, il est laissé seul. Bien que pas une résolution propre, la première solution qui vient à l'esprit est la configuration heightmap = NULL; juste après la suppression - il ne devrait pas être nécessaire, mais ne peut sûrement pas blesser pendant que vous cherchez l'explication de la raison pour laquelle vous détruisez du terrain instance deux fois! -) [[il n'y a absolument rien dans la petite quantité de code que vous montrez qui peut nous aider à expliquer la raison de la double destruction.]]

2

En mode débogage les pointeurs sont souvent réglés sur NULL et la mémoire blocs mis à zéro. C'est la raison pour laquelle vous rencontrez un comportement différent en mode débogage/relâchement.

Je vous suggère d'utiliser un pointeur intelligent au lieu d'un pointeur traditionnel

auto_ptr<Heightmap> HeightmapLoader::load() // a static method 
{ 
    // .... 
    auto_ptr<Heightmap> heightmap(new Heightmap()); 
    // ....other code 
    return heightmap; 
} 

de cette façon que vous n'avez pas besoin de le supprimer plus tard, il sera fait automatiquement pour vous

voir aussi boost::shared_ptr

+0

Merci, je ne m'en suis pas rendu compte sur le mode debug ... J'ai l'habitude de coder en Java. Je vais regarder dans le auto_ptr! – Mossen

+0

+1, regardez aussi 'scoped_ptr <>' et 'unique_ptr <>', à partir de boost ou de std :: tr1. Ce sont des solutions différentes pour la gestion de la mémoire du pointeur intelligent. –

0

Cela ressemble au cas classique du pointeur non initialisé. Comme dit @Greg, que faire si load() n'est pas appelé depuis Terrain? Je pense que vous n'initialisez pas le pointeur HeightMap* à l'intérieur du constructeur Terrain. En mode débogage, ce pointeur peut être défini sur NULL et C++. La suppression d'un pointeur NULL est une opération valide et donc le code ne plante pas. Cependant, en mode release en raison d'optimisations, le pointeur n'est pas initialisé et vous essayez de libérer un certain bloc de mémoire aléatoire et l'incident ci-dessus se produit.