SMART POINTEUR À COMPTEUR DE RÉFÉRENCE

cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 - 9 déc. 2009 à 11:16
BloodReapR Messages postés 6 Date d'inscription vendredi 9 janvier 2009 Statut Membre Dernière intervention 16 décembre 2009 - 16 déc. 2009 à 19:16
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/50948-smart-pointeur-a-compteur-de-reference

BloodReapR Messages postés 6 Date d'inscription vendredi 9 janvier 2009 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 19:16
>> 1) L'inlining est très efficace (transformation d'une fonction en >>uniquement son corps, donc pas d'appel).

Oui je sais apropos du inline mais d'après ce que je me souviens, pour Windows ou Linux (ou tout autre OS) c'est un appel de fonction spécifique (du OS) pour l'allocation de mémoire heap. Ce qui rend la fonction non-inlinable. Mais comme j'ai dit c'est d'après ce que je me souviens.
BloodReapR Messages postés 6 Date d'inscription vendredi 9 janvier 2009 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 19:05
Puisque je construis ma propre classe de smart_ptr (pour des raisons de simplicité et apprentissage) j'ai remarqué que ta fonction release ne fait pas le boulot qu'elle est censée faire (selon le STL). Release en fait devrait avoir comme valeur de retour le pointeur de la memoire alloqué et rendre le pointeur membre "NULL". Ex:

inline T * TSmartPtr<T, Array>::Release()
{
T *pPtr = mpPointer;
mpPointer = NULL; // ou '0' si vous préferez
return pPtr;
}
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
16 déc. 2009 à 17:45
> La différence je sais n'est pas grande mais moi dans ma tête je me dis vaut mieux faire un if qu'un appel de fonction.
Bof.
1) L'inlining est très efficace (transformation d'une fonction en uniquement son corps, donc pas d'appel).
2) De plus, dans le cas ou le pointeur est différent de null, ce qui est quand même très souvent le cas, tu fais deux fois la vérification.
BloodReapR Messages postés 6 Date d'inscription vendredi 9 janvier 2009 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 17:41
Oui NULL est un macro de 0, je l'utilize seulement dans le cas des pointeurs pour les différentier des entiers habituels. Mais apropos de la verification du pointeur avec 0 avant l'appel de delete, je disais que c'était une question de performance parce que justement t'évite te faire un appel à la fonction delete par un if. La différence je sais n'est pas grande mais moi dans ma tête je me dis vaut mieux faire un if qu'un appel de fonction.
nirgal76 Messages postés 2 Date d'inscription jeudi 6 septembre 2007 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 16:58
Oui je vais remédier à ça. plutot en l'empêchant d'ailleurs comme c'est une fonctionnalité que je ne vais pas utiliser.

pour NULL, c'est vrai mais j'ai pris l'habitude avec l'aide et les exemples de builder à toujours voir des NULL et du coup, j'ai pris le mauvais pli :/

Merci en tout cas
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
16 déc. 2009 à 16:53
> c'est en Builder C++
C'est plutôt une recommendation générale sur le C++ qui n'est pas restreinte à Builder C++. (Faite par le créateur du C++ lui même). Ce ne provoquera pas d'erreur, c'est une juste une question de style.

> ben le but est de s'amuser à en refaire un, c'est quand comme ça que l'on apprends le mieux.
Absolument, c'est légitime. Le code est propre et bien commenté, mais il faudrait tout de même expliquer dans la descriptions que c'est un code à but didactique, et que la STL est à privilégier.

> même si je ne l'utilise jamais dans le cas de l'exemple qui provoque une fuite
Enfin, le cas que je t'ai présenté reste un bug, qu'il serait sympa de corriger, ou tout du moins d'empêcher :).
Soit dit en passant, ça ne provoque pas vraiment une fuite, mais une tentative d'accès mémoire invalide (libération multiples du même espace mémoire).
nirgal76 Messages postés 2 Date d'inscription jeudi 6 septembre 2007 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 16:43
>Il existe des std::smart_ptr, autant les utiliser !
ben le but est de s'amuser à en refaire un, c'est quand comme ça que l'on apprends le mieux.

pour l'utilisation de NULL: selon le lien http://cpp.developpez.com/faq/bcb/?page=langage#langagenull
aucun soucis à l'utiliser puisque c'est en Builder C++.

le if (NULL!=p) :
Oui, delete fait le meme test donc aucun gain en performance si le pointeur est à NULL, et baisse en performance même s'il n'est pas à NULL car le test sera fait 2 fois. La seule utilité serait si delete était redéfini et sans le test...mais ça serait une erreur.
à l'origine, ce SAFE_DELETE, c'est une macro que j'avais repris du sdk directx sans vraiment m'en préoccuper. j'aurais du ;)

En bref, merci pour vos commentaires, le but était de me faire corriger mon code ;). Cela me sera très utile (même si je ne l'utilise jamais dans le cas de l'exemple qui provoque une fuite).
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
16 déc. 2009 à 16:02
C'est faux. Je te rappelle que delete et new sont des opérateurs (qui peuvent d'ailleurs être redéfinis). Ce sont donc des fonctions. Le cas if (p != NULL) est testé *dans* le delete. Inutile de le remettre et non il n'y a pas de différence de performance.
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.8

A noté que 0 devrait être employé plutôt que NULL.
http://cpp.developpez.com/faq/bcb/?page=langage#langagenull
BloodReapR Messages postés 6 Date d'inscription vendredi 9 janvier 2009 Statut Membre Dernière intervention 16 décembre 2009
16 déc. 2009 à 15:53
Pour le "if (NULL!=p) { delete p; p=NULL; }" je crois qu'il devrait rester comme ça. Parce qu'un appel direct à delete sans en avoir besoin prend plus de ressource qu'un simple if. C'est juste une petite question de performance.
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
9 déc. 2009 à 11:16
Et encore un smart pointer défecteux !
Il existe des std::smart_ptr, autant les utiliser !

#define SAFE_DELETE(p) { if (NULL!=p) { delete p; p=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if (NULL!=p) { delete[] p; p=NULL; } }

Ca c'est con, parce que "delete NULL;" est valide et ne fait rien. "if (NULL!=p) { delete p; p=NULL; }" peut être remplacé par: "delete p; p=NULL;"

Ensuite, ton code leak !!! Il ne détruit pas correctement tout ce qu'il faut.

Exemple:
#include "smart.hh"
#include

int main()
{
TSmartPtr mpMaClasse;
TSmartPtr mpMaClasse2;

int* a = new int;

mpMaClasse = a;
*mpMaClasse = 45;

std::cout << *mpMaClasse << std::endl;

mpMaClasse2 = a;
*mpMaClasse2 = 18;

std::cout << *mpMaClasse << std::endl;
std::cout << *mpMaClasse2 << std::endl;

return 0;
}

Ici, a est libéré deux fois. Ca ne se voit pas forcément, mais en passant un coup de valgrind:
valgrind ./a.out
4527== Memcheck, a memory error detector
4527== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
4527== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
4527== Command: ./a.out
4527==
45
18
18
4527== Invalid free() / delete / delete[]
4527== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
4527== by 0x8048A1A: TSmartPtr::Release() (in /tmp/a.out)
4527== by 0x804895E: TSmartPtr::~TSmartPtr() (in /tmp/a.out)
4527== by 0x80488B1: main (in /tmp/a.out)
4527== Address 0x42bb028 is 0 bytes inside a block of size 4 free'd
4527== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
4527== by 0x8048A1A: TSmartPtr::Release() (in /tmp/a.out)
4527== by 0x804895E: TSmartPtr::~TSmartPtr() (in /tmp/a.out)
4527== by 0x804888D: main (in /tmp/a.out)
4527==
4527==
4527== HEAP SUMMARY:
4527== in use at exit: 0 bytes in 0 blocks
4527== total heap usage: 3 allocs, 4 frees, 12 bytes allocated
4527==
4527== All heap blocks were freed -- no leaks are possible
4527==
4527== For counts of detected and suppressed errors, rerun with: -v
4527== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 19 from 8)

En effet, tu ne partages pas de refCounter entre tes classes. Donc ca ne sert à rien.
La seule solution viable, est d'avoir un flyweight. C'est-à-dire une collection statique partagée par toutes les instances de ta classe smart, et dans lequel tu partages tous ce qui est référencé. Un compteur un tenu pour chaque élément. Ainsi, un élément ne sera détruit que si plus personne ne pointe dessus, quelque soit l'endroit dans le code, et même avec deux instances différentes de ta classe smart.
Rejoignez-nous