OVH Cloud OVH Cloud

Liberation de tableau et de classe

5 réponses
Avatar
Michael Moreno
Bonjour,

Pour liberer un tableau alloue dynamiquement, vous faites comment svp ?

Pour ma part, je fais toujours ceci :

if (MonTableau != NULL)
{
delete MonTableau;
MonTableau = NULL;
}

Mais je pense qu'une template serait la bienvenue comme la suivante :

template <typename T>
FreeAndNil(T [] anArray)
{
if (anArray != NULL)
{
delete anArray;
anArray = NULL;
}
}

De meme pour tout object alloue dynamiquement, je pourrai avoir une
fonction du genre :
FreeAndNilObj( void * obj)
{
if (obj != NULL)
{
delete obj;
obj = NULL;
}
}

Ca vous semble une bonne solution ou non ?

Merci pour votre aide.

Michael

--
----
http://michael.moreno.free.fr/

5 réponses

Avatar
Jean-Marc Bourguet
Michael Moreno writes:

Bonjour,

Pour liberer un tableau alloue dynamiquement, vous faites comment svp ?

Pour ma part, je fais toujours ceci :

if (MonTableau != NULL)
{
delete MonTableau;
MonTableau = NULL;
}


* J'utilise generalement vector<> ou deque<>.
* Si c'est un tableau, c'est delete[].
* Le test a NULL est inutile, delete se comporte correctement dans ce
cas.
* L'assignation de NULL a MonTableau est generalement inutile: ou je
suis en train de retailler le tableau et il y a une autre assignation
plus utile qui suit, ou je suis dans un destructeur. Il y a bien
quelques cas ou c'est utile (je pense a des caches et a des calculs
paresseux) mais c'est alors par conception.

Mais je pense qu'une template serait la bienvenue comme la suivante :

template <typename T>
FreeAndNil(T [] anArray)


C'est pas du C++. Tu veux vraissemblablement ecrire
FreeAndNil(T*& anArray)
{
if (anArray != NULL)
{
delete anArray;
delete[]

anArray = NULL;
}
}

De meme pour tout object alloue dynamiquement, je pourrai avoir une
fonction du genre :
FreeAndNilObj( void * obj)
{
if (obj != NULL)
{
delete obj;
Et tu perds l'appel au destructeur.

obj = NULL;
}
}

Ca vous semble une bonne solution ou non ?


Comme dit ci-dessus, mes utilisations de pointeurs sont telles qu'une
remise systematique a NULL est inutile.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
xavier
Michael Moreno wrote:
template <typename T>
FreeAndNil(T [] anArray)


Si ton tableau est alloué dynamiquement, tu obtiens un pointeur sur le
tableau. Donc la déclaration serait plutôt :

template <typename T> FreeAndNil(T * & anArray)

Tu as besoin de passer une référence à ton pointeur puisque tu souhaite
changer sa valeur au dela de la portée de ta fonction.

delete anArray;


Si tu alloue un tableau via l'opérateur new [], tu dois libérer par le
destructeur delete [] :

delete[] anArray;

FreeAndNilObj( void * obj)


Pourquoi n'en fait tu pas un template ? En utilisant systèmatiquement un
pointeur sur void *, le destructeur ne sera pas appelé.

delete obj;


L'appel de delete sur un pointeur void est un comportement indéfini.

Donc :

template <typename T>
FreeAndNilObj(T * & obj) {
if(obj) {
delete obj;
obj = 0;
}
}

Ca vous semble une bonne solution ou non ?


Personnellement, j'utiliserais en priorité les conteneurs de la STL pour
tout utilisation de tableau dynamique.

Ensuite, pour ce destructeur, je ferais :

template <bool is_an_array, typename T>
bool FreeAndNil(T * & a) {
if(a) {
if(is_an_array) delete[] a;
else delete a;
a = 0;
return true;
}
return false;
}

#if TEST
#include <iostream>
#include <cassert>

using namespace std;

class test {
public:
virtual ~test() {
cout << "destructeur de " << (void*)this << endl;
}
};

bool FreeAndNilObj_bad(void * & a) {
if(a) {
delete a;
a = 0;
return true;
}
return false;
}


int main() {
int * a, * b;

a = new int[100];
b = new int;

cout << "a == " << (void*)a << endl;
cout << "b == " << (void*)b << endl;

assert (a != 0 && b != 0);
assert (FreeAndNil<true>(a));
assert (FreeAndNil<false>(b));
assert (a == 0 && b == 0);

cout << "a == " << (void*)a << endl;
cout << "b == " << (void*)b << endl;

test * c, * d;

c = new test;
d = new test;

FreeAndNil<false>(c); // affiche "destructeur de ..."
FreeAndNilObj_bad(d); // n'affiche rien

}
#endif

Avatar
Michael Moreno
Merci beaucoup a vous deux pour votre aide precieuse.

Et en effet, je ne veux pas toujours utiliser la STL.

--
----
http://michael.moreno.free.fr/
Avatar
Ivan Vecerina
"xavier" wrote in message
news:41caa53b$0$20290$
...
Donc :

template <typename T>
FreeAndNilObj(T * & obj) {
if(obj) {
delete obj;
obj = 0;
}
}
La norme C++ spécifie très clairement que:

delete 0;
est une opération valide et sans aucun effet.

Le test if(obj) est donc inutile et redondant:
template <typename T>
FreeAndNilObj(T * & obj)
{
delete obj;
obj = 0;
}

Ceci dit, en C++, à cause des exceptions en particulier,
il est déraisonnable d'utiliser (encore) ce genre de
fonctions, et pratiquement illusoire de penser qu'elles
puissent être utilisées correctement.

Ca vous semble une bonne solution ou non ?


Personnellement, j'utiliserais en priorité les conteneurs de la STL pour
tout utilisation de tableau dynamique.
Et un "smart pointer" comme std::auto_ptr ou (boost/tr1)::shared_ptr

pour quasi toute utilisation d'un objet alloué dynamiquement.

Ensuite, pour ce destructeur, je ferais :

template <bool is_an_array, typename T>
bool FreeAndNil(T * & a) {
Fort dangereux, vu la possibilité de passer une valeur

incorrecte à is_an_array lors d'une invocation.

int main() {
int * a, * b;

a = new int[100];
b = new int;

cout << "a == " << (void*)a << endl;
cout << "b == " << (void*)b << endl;
assert (a != 0 && b != 0);
assert (FreeAndNil<true>(a));
assert (FreeAndNil<false>(b));
assert (a == 0 && b == 0);
Problèmes:

1) en mode release, compilé avec NDEBUG (ce qui
désactive 'assert'), on se retrouve avec une
fuite de mémoire...
2) en cas de lancement d'une exception, une
fuite de mémoire surviendra également.

En comparaison:
{
std::vector<int> a(100);
std::auto_ptr<int> b = new b;
cout << "a == " << (void*)&a.front() << endl;
cout << "b == " << (void*)b.get() << endl;
}
Concis et sûr.

Franchir le pas pour utiliser la librairie standard
demande un effort, surtout si l'on a commencé avec
du C ou un autre language. Mais ça en vaut la peine...


Bon Noël,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form


Avatar
xavier
Ivan Vecerina a dis le 23/12/2004 14:03:
Le test if(obj) est donc inutile et redondant:


Oui et non.

La valeur de retour de ma fonction change suivant le cas, le test n'est
donc pas inutile.

Fort dangereux, vu la possibilité de passer une valeur
incorrecte à is_an_array lors d'une invocation.


En partant du principe qu'on souhaite utiliser deux fonctions
différentes pour faire cette opération, je trouve qu'il est plus rapide
de se tromper de nom de fonction (et donc d'appeller le mauvais
operateur) que de se tromper entre <true> et <false>. L'absence de
valeur par défaut oblige l'utilisateur de la fonction a choisir l'un ou
l'autre et à se poser la question de celui qui est nécéssaire.

Problèmes:
1) en mode release, compilé avec NDEBUG (ce qui
désactive 'assert'), on se retrouve avec une
fuite de mémoire...


#if TEST && !defined(NDEBUG)

Si tu préfères, mais en l'occurence, je désactive rarement le mode
déboggage dans les tests. Ceci étant dit, tu as tout à fait raison de
remarquer que ce n'est pas une bonne habitude de faire cela dans un assert.