un argument que l'on entend souvent pour l'existence des smarts pointers, est de s'assurer que l'objet est effectivement détruit dès que l'on fait sur un pointeur brut (libération de la mémoire ici + destruction de l'objet). En faisant :
delete p;
on libère la mémoire.
Si l'on est dans une portée simple, la libération de la mémoire libère, en principe, d'après mes connaissances, la mémoire contenant l'objet qui peut donc être détruit si il n'y a plus rien après le delete.
Or j'ai fait un code très simple pour vor à quel moment précis intervient l'utilisation du destructeur. Je me serais attendu à ce que l'objet de la classe A soit détruit en toute fin après le dernier message d'affichage à l'écran. Et pourtant, il est détruit avant.
Donc le problème de la destruction instantanée de l'objet pointée par le pointeur ptr1 ne se pose pas du tout en apparence.
Pouvez-vous m'expliquer pour que je comprenne mieux pourquoi il est toujours mieux d' utiliser un pointeur intelligent, même dans ce cas ?
Il y a peut-être une subtilité que je n'ai pas saisie, merci de me l'expliquer.
Auriez-vous un exemple de ce genre où l'on voit que si on utilise un pointeur brut , alors la destruction définitive de l'objet intervient trop tard ?
Merci par avance
#include <iostream>
struct A {
int id;
A(){};
A(int id) : id(id) { std::cout << "A " << id << '\n'; }
~A() { std::cout << "~A " << id << '\n'; }
};
int main(){
A* ptr1 = new A(1);
std::cout<<"Premier test"<<'\n';
delete ptr1;
std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
}
Encore une fois, tu poses une question sans réfléchir.
Si je te disais qu'un code aussi simple que
int main(){
int * ptr = new int[10];
std::cout<<"salut\n";
delete ptr;
}
est fondamentalement buggé, parce que std::cout provoque une allocation dynamique de la mémoire, et que, le simple fait qu'une allocation dynamique peut échouer (et lancer une exception) empêche de garantir que delete ptr sera appelé.
Que devrais tu en déduire si tu arrivais à brancher ton cerveau (à condition que tu en aies un) ?
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
je suis d'accord pour l'argument de l'allocation dynamique qui peut échouer.
Mais si on suppose que pour l'allocation dynamique c'est OK, pourquoi dans mon exemple avec la classe A, pourquoi le delete du pointeur sur un élément de type A entraîne-t-il immédiatement l'appel du destructeur de A alors que en principe, avec delete, la mémoire vient seulement d'être libérée ?
L'objet n'est-il pas détruit en fin de scope après libération de la mémoire , donc après mon deuxième message ?
Je vous remercie par avance d'éclaircir ce point précis.
Sinon on empile les notions, sans les raccorder entre elles, on en oublie la moitié, on les comprend de traviole, on ne voit pas à quoi ça sert, et on mélange tout dans un horrible pâté.
Dans ce cas : portée d'une variable, pointeur, objet pointé, destructeur, appel explicite du destructeur, libération d'une variable à la fin d'un bloc etc.
Comme disait l'autre : que ceux qui étudient travaillent, que ceux qui travaillent étudient (confucius).
Mais si on suppose que pour l'allocation dynamique c'est OK, pourquoi dans mon exemple avec la classe A, pourquoi le delete du pointeur sur un élément de type A entraîne-t-il immédiatement l'appel du destructeur de A alors que en principe, avec delete, la mémoire vient seulement d'être libérée ?
Parce que tu ne peux pas supposer que l'allocation dynamique c'est OK!!!
La loi de l'emmerdement maximal joue contre toi : tout ce qui peut échouer échouera d'office. Il ne sert donc à rien de te demander SI quelque chose qui peut échouer échouera... Tu gagnera énormément de temps en te demandant directement QUAND ce qui peut échouer échouera.
Et la réponse est toujours la même : au pire moment qui soit!
Quant au fait que le destructeur d'une classe (ou d'une structure) soit directement appelé quand une instance de la classe est détruite, ben, ca, c'est parce que les développeurs du langage ont été intelligents (pas comme toi, donc) et qu'ils se sont dits qu'il était cohérent de faire en sorte à ce que tout ce qui doit être entrepris pour détruire l'intégralité du contenu de la classe soit entrepris au moment où l'instance de la classe sera détruite, tout simplement
- Edité par koala01 11 mars 2019 à 20:52:39
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
j'ai corrigé mon intervention pour lui faire dire quelque chose de sensé en parlant d'instance de la classe
Merci de la remarque
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Auriez-vous un exemple de ce genre où l'on voit que si on utilise un pointeur brut , alors la destruction définitive de l'objet intervient trop tard ?
Merci par avance
#include <iostream>
struct A {
int id;
A(){};
A(int id) : id(id) { std::cout << "A " << id << '\n'; }
~A() { std::cout << "~A " << id << '\n'; }
};
int main(){
A* ptr1 = new A(1);
std::cout<<"Premier test"<<'\n';
//delete ptr1; //--> Fuite de mémoire <--
std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
}
Pas trop tard, le problème serait de ne pas appeler le delete. Pour conséquence, une fuite de mémoire, ou pire encore, appeler le delete trop tôt, ce qui engendrera probablement un Seg fault lors de son utilisation.
La règle d'or: Pour chaque appel de new, ça prend un appel de delete.
En gros, c'est risqué, l'utilisation de smart_ptr, gère tout ça pour toi.
YES, man a écrit:
Or j'ai fait un code très simple pour voir à quel moment précis intervient l'utilisation du destructeur. Je me serais attendu à ce que l'objet de la classe A soit détruit en toute fin après le dernier message d'affichage à l'écran. Et pourtant, il est détruit avant.
Allocation sur la pile (stack): -Mémoire réservé d'avance, puis pile et dé-pile -Destruction automatique à la fin du scope (appel du destructeur):
#include <iostream>
struct A {
int id;
A(){};
A(int id) : id(id) { std::cout << "A " << id << '\n'; }
~A() { std::cout << "~A " << id << '\n'; }
};
int main(){
A obj(1); //appel du constructeur
std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
} //appel du destructeur
GZE, un moteur multiplateforme, adapté pour de la 2D, 3D et création de logiciels.
Un peu de bon sens, on sait que new appelle le constructeur. Partant de là, il semble censé de penser que les architectes du langage ont été cohérents dans leurs choix de conception, et donc que delete appelle le destructeur...
Du reste, comment pourrait on appeler le destructeur sur une instance d'objet dont la mémoire a déjà été libérée? N'oublions pas que le tas recycle la mémoire, donc si delete n'appelle pas le destructeur, le destructeur sera appelé sur la mémoire potentiellement affectée à une autre variable qui n'a rien de commun avec celle qui est détruite, c'est juste absurde!
L'usage de pointeurs nus pour l'allocation dynamique, pose 3 problèmes:
La fuite de mémoire, un new et pas de delete correspondant sur le chemin d'exécution.
La libération multiple, un new et plusieurs delete associés sur le même chemin d'exécution.
La réalité du terrain, on n'est pas dans le monde magique des bisounours, n'importe quelle erreur d'exécution peut survenir à n'importe quel moment et Murphy veille au grain, sa loi (dite de l'emmerdement maximum) s'applique toujours avec une "efficacité" redoutable.
L'emploi de pointeurs intelligents permet de se protéger de manière complète contre les deux premiers problèmes, et presque systématiquement contre le troisième.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.
GZE, un moteur multiplateforme, adapté pour de la 2D, 3D et création de logiciels.