Il y a une question qui me trotte dans la tête portant sur l'allocation dynamique dans un cas précis. je n'ai encore jamais eu la réponse, certainement parce que je n'ai jamais pu l'exprimer correctement. Pour me faire comprendre, je vais passer par un exemple. Supposons qu'on a la classe Exemple suivante:
class Exemple
{
public:
Exemple(t_Coordonnee *coord);
private:
int x, y;
};
t_Coordonnee est une structure qui possède deux int. Dans le main, on déclare une instance de Exemple de la manière suivante:
Exemple exemple(new t_Coordonnee());
On a donc fait une allocation dynamique de la structure t_Coordonnee dans le constructeur d'exemple. Mais dans ce cas précis, comment assurer la libération de la mémoire? Le t_Coordonnée est directement utilisé dans le constructeur et n'est pas un attribut de la classe. J'ai observé cette pratique en récupérant le code de quelqu'un et je me demande si c'est une bonne pratique ou si c'est fuite de mémoire assurée.
Merci de vos lumières
T_coordonnee est une structure quelconque. Dans le main, on déclare un
Avec l'exemple d'appel que tu as montré, c'est fuite assurée. Cela ressemble beaucoup à du Java porté syntaxiquement en C++ sans l'adapter comme il aurait d^u l'^etre surtout pour des points.
Les diverses raisons que l'on pourrait avoir à recevoir un pointeur ici
- pour éviter de copier un gros truc sur la pile. A ce compte là, il faut employer des références
- parce que la classe Exemple devient responsable de la mémoire passée en paramètre --> En 2011, le C++(11) a introduit les `std::unique_ptr` il sont faits pour cela: forcer l'appelé à devenir responsable. En plus ils garantiront la libération de mémoire.
- parce qu'il y a un c^oté optionnel --> boost::optional/std::optional apporteront une sémantique plus claire
Mais si ton truc a 2 juste coordonnées... alors clairement, il faut virer tous ces pointeurs.
C'est un exemple. En réalité il y a beaucoup de propriété dans les différentes structures utilisées.
J'avais en effet entendu parler de ces pointeurs intelligents, mais je ne m'y étais encore jamais attarder. Merci de ton aide lmghs. Je vais en apprendre plus sur les std::unique_ptr et modifier tout ce code.
...te retourne une adresse (un entier 32 ou 64 bits), l'adresse du début (l'adresse du premier octet) du bout de mémoire qu'il vient d'allouer pour contenir la valeur de ton nouvel objet t_Coordonnee. Après ça, c'est ta responsabilité de t'assurer que la mémoire à cette adresse est libérée.
Tant que tu ne perds pas le pointeur, il n'y a pas de "fuite de mémoire assurée". Peut-être que le constructeur de Exemple libère la mémoire du pointeur qu'il reçoit (ça serait monstrueux, mais c'est "possible").
Pour répondre à ta question, non, ce n'est pas une bonne pratique. Les allocations dynamiques sont lentes donc ce n'est pas non plus une optimisation. Je pense que c'est exactement comme @lmghs l'a dit et que c'est juste quelqu'un qui vient d'un langage orienté objet et qui ne comprend pas ce que fait vraiment le mot-clé new.
1) Ne pas recourir à l'allocation dynamique, et lui préférer l'allocation automatique quand c'est possible. Le recours à l'allocation dynamique ne se justifie que dans la mesure où il est difficile de déterminer la fin de vie de l'objet, ou un objet de très grande taille*. Dans une grande majorité des situation, la durée de vie de l'objet ne sort pas du scope, donc une allocation automatique sera parfaite.
Exemple ex{}; // Initialisation par defaut
Passer par des pointeurs intelligents, soit std::unique_ptr, soit le couple std::shared_ptr/std::weak_ptr. Le premier garantit l'unicité, le second permet le partage tout en garantissant la destruction a la disparition du dernier détenteur. Je te conseille d'utiliser au maximum le unique_ptr, d'abord parce qu'il est probablement mieux adapté à ton usage, ensuite parce qu'il est plus léger et plus simple (Les cas où le shared_ptr est indispensable sont assez rares).
auto ex = std::make_unique<Exemple>(/* paramètres du constructeur de Exemple */);
/*
ou bien en typant tout:
std::unique_ptr<Exemple> ex = std::make_unique<Exemple>(/* paramètres du constructeur de Exemple */);
*/
Note que tu as une fonction make_shared qui fonctionne exactement comme make_unique et qui produit un shared_ptr au lieu d'un unique_ptr.
(*) Les objets de grande taille: la première chose à savoir est qu'ils sont rarissimes, presque tout ce qui va être potentiellement gros (chaîne, tableau, arbre…) pourra quasiment toujours être stocké dans un conteneur plus ou moins standard, lequel est en principe, parfaitement capable de gérer sa mémoire comme un grand (RAII). Il ne reste donc que de très gros struct, et là il faut vraiment des trucs monstrueux pour ne pas pouvoir utiliser l'allocation automatique, l'allocation automatique se fait sur la pile d'exécution qui fait quand même 1 Mo ou plus sur nos ordinateurs modernes.
× 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.
Discord NaN. Mon site.