C++ Pb de libération de memoire. - Programmation
Marsh Posté le 12-03-2001 à 10:17:30
il faudrait plus de precisions :
-> structure de la liste chainée
-> comment est créé l'objet
-> comment tu fais pour libérer la mémoire
Marsh Posté le 12-03-2001 à 15:04:13
Je n'ai pas le source sous les yeux (ce soir, je le mettrais), mais c'est simple, ma liste chainee prend un pointeur (void*) vers un objet quelconque.
je fais :
Obj* monObj = new Obj();
maliste->add((void*)monObj);
et voila, j'ai un objet à la suite de ma liste.
Pour le libérer, j'ai une méthode qui parcourt la liste et qui fait : delete monObj;
sachant que monObj et l'objet qui se trouve à la fin de la liste. Ensuite, on remonte jusqu'au debut en désallouant l'objet au passage.
Ca me plante méchamment à la gueule. Bouhhhh.
Marsh Posté le 12-03-2001 à 15:10:44
Ben c'est comme ça qu'on fait en principe. Donc tu dois avoir fait une erreur dans le code.
Marsh Posté le 12-03-2001 à 15:12:51
C'est normal : comment veux-tu libérer un void* ?
Si delete est utilisé avec un scalaire (int, char, float etc...), il libère l'espace mémoire
S'il est appelé avec une classe, c'est le destructueur (virtuel) de la classe qui est appelé.
Void n'est pas le "type à tout faire" mais au contraire, une absence de type.
Pour résoudre ton problème, il faut que les atomes de ta liste dérivent d'une classe avec un destructeur virtuel. Ensuite, chaque sous-classes pourra spécialiser son destructeur : c'est le but de la programmation objet !
Marsh Posté le 12-03-2001 à 15:20:01
certes, je fais un delete sur un void, mais comme tous les objets que j'insere derivent d'une classe A de base, je fais un cast avant, comme ceci : delete (A*)monObj.
Mais apparemment, il ne libère pas toute la mémoire, mais seulement celle que prend un objet A et non pas celle d'un objet B qui dérive (et qui est + gros).
Toi, tu voudrais qu'au lieu d'un pointeur de type void je mette un pointeur vers un objet de base dont la classe possede un destructeur ?? je vois pas trop.
Ha, sinon, un objet ne peut pas par hazard se libérer à la fin de son destructeur ?
Marsh Posté le 12-03-2001 à 15:24:22
>j'insere derivent d'une classe A de base, je fais un cast
>avant, comme ceci : delete (A*)monObj.
>Mais apparemment, il ne libère pas toute la mémoire, mais
>seulement celle que prend un objet A et non pas celle d'un
>objet B qui dérive (et qui est + gros).
Ah ok, c'est bon alors. S'il appelle le destructeur du type du pointeur et non le destructeur du type de la classe, c'est que ton destructeur n'est pas virtuel : tu as vérifié ?
Marsh Posté le 12-03-2001 à 15:28:27
Haaaa, c'est pas con ca. Mais il faudrait qu'un objet puisse lui meme dans son code se desallouer. Donc, est ce que je peux mettre la ligne : delete this à la fin du destructeur d'un objet. C'est un peu bizarre, mais ca serait tellement pratique.
Marsh Posté le 12-03-2001 à 15:36:31
delete this dans le destructeur est une mauvaise idée puisque delete appelle le destructeur
[edit]--Message édité par Verdoux--[/edit]
Marsh Posté le 12-03-2001 à 15:41:09
Il faut virer le void* de la def de ta liste. Tu peux utiliser un template pour ça ( ou alors carrément utiliser les listes déjà définies dans la STL).
Et puis toujours mettre un destructeur virtuel dans une classe de base (dans 99% des cas).
Marsh Posté le 12-03-2001 à 15:42:11
>Mais il faudrait qu'un objet puisse lui meme dans son code se
>desallouer. Donc, est ce que je peux mettre la ligne : delete
>this à la fin du destructeur d'un objet. C'est un peu bizarre,
>mais ca serait tellement pratique.
Je comprends pas vraiment ton probleme. le keyword delete appelle justement le destructeur d'une classe et libere l'espace mémoire aloué. Tu n'as pas besoin de faire "delete this" (j'ajouterais qu'il ne faut pas sinon ca part en recursion, enfin j'ai pas testé).
Tu veux faire des objets qui se liberent automatiquement, il faut que tu codes un système de "garbage collecting" à la java.
Une solution plus simple qui evite les parcours dans tous les sens d'une liste, c'est d'abonner au moment de la création chaque objet créé à une classe qui va stocker les pointeurs vers les instances courantes. Tu pourra ainsi les effacer quand tu veux.
Marsh Posté le 12-03-2001 à 15:45:06
Haaaaaa, delete, j'avais pas pigé. Et que fais tu dans ton destructeur ? (mis a part les actions eventuelles sur d'autres objets?)
Tu tapes quoi, par exemple ?
Marsh Posté le 12-03-2001 à 15:50:26
"Il faut virer le void* de la def de ta liste. Tu peux utiliser un template pour ça ( ou alors carrément utiliser les listes déjà définies dans la STL). "
=====> les templates, j'y ai pas encore touché. t'as un exemple, stp.
=====> c'est quoi la STL (oui, je ne sais pas grand chose en C++, mais bon, mon rayon c'est + le java).
"Et puis toujours mettre un destructeur virtuel dans une classe de base (dans 99% des cas). "
=====> ca OK, je vais le faire. Mais le pb, c'est que j'ai des tonnes de classes dérivées. Un bonne quinzainne... ca fait chier de redefinir tant de fois un destructeur.
Marsh Posté le 12-03-2001 à 16:03:46
oliv5> voici un exemple:
class MaClass {
protected:
int unEntier;
float unFloat;
int* unPointeur;
int* unTableau; // un pointeur sur un objet créé dans la classe
Obj obj1;
Obj* obj2; // un pointeur sur un objet créé dans la classe
public:
MaClass() {
unTableau = new int[10];
obj2 = new Obj;
}
virtual ~MaClass() {
delete[] unTableau;
delete obj2;
// tout le reste est libéré automatiquement
}
}
Marsh Posté le 12-03-2001 à 16:08:06
Et il faudrait planquer le constructeur par copie et l'opérateur d'affectation sinon ça peut merder.
Marsh Posté le 12-03-2001 à 16:30:26
Les templates ou patrons de classes permettent de définir des familles de classes à partir d'une seule, grâce à une paramétrisation.
La STL est un ensemble de telles classes. (voir http://www.sgi.com/tech/stl/ )
Par exemple:
list<int> liste_d_entiers; // est une liste contenant des entiers.
Et list<Objet*> liste_d_objets est une liste de pointeurs sur des Objets.
Pour remplir ma liste_d_entiers:
for(int = 0; i < 10; i++)
list_d_entier.push_back(i);
Pour la pourcourir:
for(list<int>::iterator it = liste_d_entiers.begin(); it != liste_d_entiers.end(); ++it)
cout << *it << endl;
Là on voit apparaître un type d'objet liés aux classes de conteneurs de la STL, les itérateurs.
Un peu comme un pointeur pour les listes "classiques" ou les tableaux, un itérateur permet de parcourir les éléments d'un conteneur.
[edit]--Message édité par Verdoux--[/edit]
Marsh Posté le 14-03-2001 à 15:54:55
y a un truc qui me chifonne...
Citation : |
en clair tu as ca:
derivation
objetA ------------> objetB
et apres tu fais ca:
objetB* monObj = new objetB();
maliste->add((void*)monObj);
et pour effacer monObj tu fais dans ta liste:
delete (objetA*)monObjDansLaListe;
et apres tu trouves illogique que la liberation memoire n a pas ete effectue correctement... mais si tu regardes bien c normal...
1- la declaration objetB doit surrement contenir plus de proprietes que objetA (par derivation sinon pas la peine de le faire), donc quand tu fais delete (objetA*)monObjDansLaListe tu detruis un objet de de type objetA... donc destruction qui ne prend pas en compte les definitions de l objetB
2- comme j ai dit precedemment tu fais appel au destructeur de l objetA au lieu de destructeur de l objetB ... ce qui est pas logique
Marsh Posté le 14-03-2001 à 16:08:51
>2- comme j ai dit precedemment tu fais appel au destructeur de >l
>objetA au lieu de destructeur de l objetB ... ce qui est pas
>logique
C'est tout à fait logique si le destructeur est virtuel.
Marsh Posté le 17-03-2001 à 14:53:18
Bouhouhouhou, j'y comprend rien du tout. J'ai essayé plein de trucs, dont les destructeurs virtuels qui semblaient logiques, mais ca ne marche pas comme je veux. Alors j'ai essayé un truc tout con : j'appelle delete sur une classe toute simple (pas d'heritage, de derivation ou quoi que ce soit) et malgré ca, l'objet est encore en mémoire (je peux lire ses variables etc...)
je ne comprend pas.
Marsh Posté le 17-03-2001 à 15:04:54
Je pense que même si tu libères la mémoire, en inspectant ensuite la zone occupée par l'objet, tu pourras lire ses variables dans le dernier état occupé.
Simplement cette zone n'est plus utilisée et libre pour des allocations ultérieures.
Marsh Posté le 17-03-2001 à 15:13:41
j'espère que tu as raison. Mais j'ai beau faire plein de création d'objets apres, il n'ecrase pas l'objet désalloué.
Marsh Posté le 17-03-2001 à 16:17:23
J'ai trouvé !!!!!!! En fait, dans ma liste chainée d'objets, un même objet peut être en double, triple, etc... et quand je libère la mémoire la première fois, ca marche, mais la deuxième .... ca plante.
Marsh Posté le 17-03-2001 à 18:09:19
Dans ce cas il faut utiliser des "pointeurs intelligents" (avec compteur de référence)
Marsh Posté le 17-03-2001 à 20:26:05
des pointeurs quoi ???
bon, expliques.
Pour le moment, j'ai fait une liste chainee des objets deja libérés et ca a l'air de marcher. MAis bon, apres il faut libérer la liste chainee... alors si tu as une autre solution, ca me va.
Marsh Posté le 18-03-2001 à 16:48:07
Deja regarde quels sont tes besoins!
si tu peux placer des copies d'objets dans
ta liste, pas besoin de compter les references.
(chaque clone d'objet ne sera physiquement present qu'en un seul endroit de ta liste)
Si vraiment tu ne peux pas faire autrement, ce que tu peux faire c'est faire en sorte que les objets que tu places dans ta liste realisent une interface "comptable" avec un entier compteur, et deux methodes inc_reference, dec_reference. Ainsi lorsqu'un objet est ajoute a la liste on incremente son compteur et lorsqu'il est enleve, on decremente son compteur, ainsi lorsque ce compteur arrive a zero tu sais qu'il n'y a plus de references a cet objet et c'est la que tu fais le delete.
Si tu cherches bien tu trouveras des implantations plus complexes de smart-pointers sur le web.
A+
LeGreg
Marsh Posté le 12-03-2001 à 10:06:27
C'est un problème tres con, mais bon, y en a marre : impossible de libérer un objet qui se trouve dans une liste chainee.
Bon, c'est trop con, alors vite, répondez.