OVH Cloud OVH Cloud

gestion mémoire

33 réponses
Avatar
Nicolas Aunai
salut,

j'ai un petit probleme avec la gestion d'une mémoire allouée
dynamiquement dans une fonction

vect &
vect::operator+(const vect &v) const
{
vect *p = new vect;
for(unsigned int i=0; i<x.size(); i ++)
{
*(p->x[i]) = (*x[i] + *(v.x[i]));
}
return *p;
}


imaginons que 'vect' soit un objet comprenant un vector 'x' qui lui
même contient un ensemble de pointeurs sur des objets dont la nature
nous importe peut (nous savons qu'ils ont un opérateur '+' défini)

je veux définir l'opérateur '+' pour mon objet 'vect', qui devra
additionner tous les objets pointés par son vector 'x' avec ceux
pointés par un autre vect.

pour celà j'alloue dynamiquement un espace mémoire de type 'vect', puis
je boucle sur le tableau, en espérant que cette ligne a l'intérieur de
la boucle soit correcte...

puis je retourne une référence sur mon objet dynamique avec 'return
*p;'


maintenant imaginons que j'utilise cet opérateur dans une autre
fonction :


{

vect a,b,c;

a = b + c;

}


comment a la sortie de la fonction libérer la mémoire alouée ??


merci

--
Nico,
http://astrosurf.com/nicoastro
messenger : nicolas_aunai@hotmail.com

10 réponses

1 2 3 4
Avatar
Franck Branjonneau
Nicolas Aunai écrivait:

j'ai un petit probleme avec la gestion d'une mémoire allouée
dynamiquement dans une fonction

vect &
vect::operator+(const vect &v) const
{
vect *p = new vect;
for(unsigned int i=0; i<x.size(); i ++)
{
*(p->x[i]) = (*x[i] + *(v.x[i]));
}
return *p;
}


imaginons que 'vect' soit un objet comprenant un vector 'x' qui lui
même contient un ensemble de pointeurs sur des objets dont la nature
nous importe peut (nous savons qu'ils ont un opérateur '+' défini)

je veux définir l'opérateur '+' pour mon objet 'vect', qui devra
additionner tous les objets pointés par son vector 'x' avec ceux
pointés par un autre vect.

pour celà j'alloue dynamiquement un espace mémoire de type 'vect',
puis je boucle sur le tableau, en espérant que cette ligne a
l'intérieur de la boucle soit correcte...


Tout depend de ce que fait :

new vect

Mais je doute que p->x.size() soit non nul. Utilise

*(p->at(i)) = (*x[i] + *(v.x[i]));

puis je retourne une référence sur mon objet dynamique avec 'return
*p;'


Pourquoi ne pas retourner le pointeur ?

maintenant imaginons que j'utilise cet opérateur dans une autre
fonction :


{

vect a,b,c;

a = b + c;

}


comment a la sortie de la fonction libérer la mémoire alouée ??


delete & a

mais ce n'est pas une bonne idée. Voir std::auto_ptr<>. Ou mieux
utiliser le principe RAI et faire de x un pointeur sur vector.
--
Franck Branjonneau

Avatar
Nicolas Aunai
Il se trouve que Franck Branjonneau a formulé :

Tout depend de ce que fait :

new vect





j'ai rien touché à 'new'...




Mais je doute que p->x.size() soit non nul. Utilise

*(p->at(i)) = (*x[i] + *(v.x[i]));




ok mon vector sera vide... mais pourquoi utiliser at() ? si mon vector
est vide je devrai plutôt utiliser :

p->x->push_back((*x[i] + *(v.x[i])));


non ?



puis je retourne une référence sur mon objet dynamique avec 'return
*p;'


Pourquoi ne pas retourner le pointeur ?



pour savoir comment on fait avec les références, j'ai encore un peu de
mal avec elles...



comment a la sortie de la fonction libérer la mémoire alouée ??


delete & a



ah bon... comprendrai jamais rien a new/delete...


mais ce n'est pas une bonne idée. Voir std::auto_ptr<>.


connait pas... je regarde

Ou mieux
utiliser le principe RAI et faire de x un pointeur sur vector.



RAI ?

--
Nico,
http://astrosurf.com/nicoastro
messenger :


Avatar
Loïc Joly
Nicolas Aunai wrote:

salut,

j'ai un petit probleme avec la gestion d'une mémoire allouée
dynamiquement dans une fonction

vect &
vect::operator+(const vect &v) const


En général, on déclare l'opérateur + en dehors de la classe. Ceci pour
des raisons de symétrie (sinon, le premier terme doit être un vect, et
le deuxième un truc castable en vect, ce qui rompt la symétrie de
l'addition) (et on défini operator+ à partir de operator+=).

Ensuite, l'opérateur devrait retourner un objet, et non pas une
référence ou un pointeur. Ca peut poser des problèmes de performance,
puisque du coup l'objet va être copié, mais ce problème peut être réduit :

- Certains compilateur implémentent la RVO (return value optimisation),
qui leur permet d'éviter une copie dans ce genre de cas.
- Il est possible de retourner non pas un vecteur mais un objet qui se
souvient de l'opération effectuée, et ne va réellement additionner que
quand on va accèder au contenu, tout en faisant cette gymnastique à la
compilation (voir par exemple blitz::tiny_vector sur www.oonumerics.org,
se munir d'une aspirine)
- Certains travaillent à founir au C++ une sémantique de déplacement en
plus de la sémantique de copie omniprésente actuellement. Si ton
programme peut attendre quelques années... ;)
- Dans certains cas, une fonction void add (vect const &A, vect const
&B, vect &Result) peut aider, mais l'écriture n'est vraiment pas belle.


--
Loïc

Avatar
Nicolas Aunai
Loïc Joly a exprimé avec précision :

vect &
vect::operator+(const vect &v) const


En général, on déclare l'opérateur + en dehors de la classe. Ceci pour des
raisons de symétrie (sinon, le premier terme doit être un vect, et le
deuxième un truc castable en vect, ce qui rompt la symétrie de l'addition)
(et on défini operator+ à partir de operator+=).



ah bon, tiens donc....


Ensuite, l'opérateur devrait retourner un objet, et non pas une référence ou
un pointeur. Ca peut poser des problèmes de performance, puisque du coup
l'objet va être copié,


en effet !

mais ce problème peut être réduit :

- Certains compilateur implémentent la RVO (return value optimisation), qui
leur permet d'éviter une copie dans ce genre de cas.



mouais... j'aime pas trop qu'un code dépende d'un compilo


- Il est possible de retourner non pas un vecteur mais un objet qui se
souvient de l'opération effectuée, et ne va réellement additionner que quand
on va accèder au contenu, tout en faisant cette gymnastique à la compilation
(voir par exemple blitz::tiny_vector sur www.oonumerics.org, se munir d'une
aspirine)



je sens le "trop compliqué" d'avance... :-(


- Certains travaillent à founir au C++ une sémantique de déplacement en plus
de la sémantique de copie omniprésente actuellement. Si ton programme peut
attendre quelques années... ;)


nan :-)

- Dans certains cas, une fonction void add (vect const &A, vect const &B,
vect &Result) peut aider, mais l'écriture n'est vraiment pas belle.



pas belle pas belle... c'est surtout qu'on perd l'intéret du C++ dans
l'affaire : les opérateurs surchargés... et dans ces conditions je
préfère retourner d'où je viens : le C :-)

décidément le C++ c'est pas mal, mais y'a trop de trucs ;-)

--
Nico,
http://astrosurf.com/nicoastro
messenger :


Avatar
Loïc Joly
Nicolas Aunai wrote:
Loïc Joly a exprimé avec précision :

vect &
vect::operator+(const vect &v) const



En général, on déclare l'opérateur + en dehors de la classe. Ceci pour
des raisons de symétrie (sinon, le premier terme doit être un vect, et
le deuxième un truc castable en vect, ce qui rompt la symétrie de
l'addition) (et on défini operator+ à partir de operator+=).




ah bon, tiens donc....


Ensuite, l'opérateur devrait retourner un objet, et non pas une
référence ou un pointeur. Ca peut poser des problèmes de performance,
puisque du coup l'objet va être copié,



en effet !


Mieux vaut un programme lent et correct qu'un programme rapide et faux.


mais ce problème peut être réduit :



- Certains compilateur implémentent la RVO (return value
optimisation), qui leur permet d'éviter une copie dans ce genre de cas.




mouais... j'aime pas trop qu'un code dépende d'un compilo


Le code n'en dépend pas, seule sa rapidité (à condition que tu aies joué
le jeu en définissant un constructeur de copie qui copie vraiment(*)).
Comme tout ce qui est de la rapidité d'ailleur. Tu n'est assuré de rien
en passant d'un compilo à l'autre en matière de performances.

- Il est possible de retourner non pas un vecteur mais un objet qui se
souvient de l'opération effectuée, et ne va réellement additionner que
quand on va accèder au contenu, tout en faisant cette gymnastique à la
compilation (voir par exemple blitz::tiny_vector sur
www.oonumerics.org, se munir d'une aspirine)




je sens le "trop compliqué" d'avance... :-(


Ca dépend d'où tu te place. Point de vue utilisateur, ça s'utilise de
façon transparente, point de vue implémenteur, c'est effectivement un
peu coton.

- Certains travaillent à founir au C++ une sémantique de déplacement
en plus de la sémantique de copie omniprésente actuellement. Si ton
programme peut attendre quelques années... ;)


nan :-)


Bon, dommage...

- Dans certains cas, une fonction void add (vect const &A, vect const
&B, vect &Result) peut aider, mais l'écriture n'est vraiment pas belle.


pas belle pas belle... c'est surtout qu'on perd l'intéret du C++ dans
l'affaire : les opérateurs surchargés... et dans ces conditions je
préfère retourner d'où je viens : le C :-)


Tu peux utiliser la notation avec les opérateurs partout, mais aux
endroits (rares en général) où le profiling a montré qu'il y avait
problème de performance, te rabattre sur la notation add. C'est une
micro-optimisation comme une autre.

décidément le C++ c'est pas mal, mais y'a trop de trucs ;-)



(*) Tiens, d'ailleurs, ça pourrait être drôle de faire un ioccc++, où
l'on interdit l'obfuscation par préprocesseur, puisqu'il existe des
trucs bien plus tordus en C++ :)

--
Loïc



Avatar
Loïc Joly
Nicolas Aunai wrote:

Il se trouve que Franck Branjonneau a formulé :

Pourquoi ne pas retourner le pointeur ?


pour savoir comment on fait avec les références, j'ai encore un peu de
mal avec elles...


Lire par exemple l'article 31 (Item 31: Never return a reference to a
local object or a dereferenced pointer initialized by new within the
function.) de effective c++ de Meyers (et puis lire le reste du bouquin
aussi ;))

mais ce n'est pas une bonne idée. Voir std::auto_ptr<>.



connait pas... je regarde


Bof, c'est hyper piègeur. J'aime pas du tout. Tu connais beaucoup de
types comme ça :

int f(T c)
{
}

int main()
{
T a;
T b(a);
assert (a == b); // Et non...

cout << b.data << endl;
f(b);
cout << b.data << endl; // boum !

T const c;
T d = c; // Non plus !
}



Ou mieux
utiliser le principe RAI et faire de x un pointeur sur vector.


RAI ?


RAII = Ressource acquisition is initialisation

En gros, dès que tu as besoin d'acquérir une ressouce (mémoire, handle,
sémaphore...), tu le fais dans un contructeur, et tu la libère dans le
destructeur ; ensuite, tu crées une variable locale du type
correspondant. Intérêt premier : Tu es certain que la ressource sera
libérée quoi qu'il arrive.

--
Loïc


Avatar
Franck Branjonneau
Nicolas Aunai écrivait:

Il se trouve que Franck Branjonneau a formulé :

Mais je doute que p->x.size() soit non nul. Utilise

*(p->at(i)) = (*x[i] + *(v.x[i]));




ok mon vector sera vide... mais pourquoi utiliser at() ? si mon vector
est vide je devrai plutôt utiliser :

p->x->push_back((*x[i] + *(v.x[i])));


non ?


Oui. Mais tu t'interrogeais sur ta boucle. at() te fournissait la réponse.
--
Franck Branjonneau


Avatar
Franck Branjonneau
Loïc Joly écrivait:

[std::auto_ptr<>]

Bof, c'est hyper piègeur. J'aime pas du tout.


Nest-ce pas la solution canonique à la gestion de la mémoire allouée
localement ?

Tu connais beaucoup de types comme ça :

int f(T c)
{
}

int main()
{
T a;
T b(a);
assert (a == b); // Et non...

cout << b.data << endl;
f(b);
cout << b.data << endl; // boum !

T const c;
T d = c; // Non plus !
}


Au moins un ;-). Il n'empêche, je préfère

std::auto_ptr< Type >
foo();

à

// L'appelant est responsable de la libération de la mémoire.
Type *
foo();
--
Franck Branjonneau

Avatar
Loïc Joly
Franck Branjonneau wrote:

Loïc Joly écrivait:

Tu connais beaucoup de types comme ça :

[...]




Au moins un ;-). Il n'empêche, je préfère

std::auto_ptr< Type >
foo();

à

// L'appelant est responsable de la libération de la mémoire.
Type *
foo();



Et moi, j'ai plusieurs ordres de grandeur de préférence pour

boost::shared_ptr<Type>
foo(); // Propriété partagée

Ou encore
boost::scoped_ptr<Type>
foo(); // Propriété unique, copies interdites

Voire même
monNamespaceAMoi::value_ptr<Type>
foo(); // Propriété unique duplication lors d'une copie

--
Loïc


Avatar
kanze
Loïc Joly wrote in message
news:<cbcn6c$eee$...
Nicolas Aunai wrote:


[...]
RAI ?


RAII = Ressource acquisition is initialisation

En gros, dès que tu as besoin d'acquérir une ressouce (mémoire,
handle, sémaphore...), tu le fais dans un contructeur, et tu la libère
dans le destructeur ; ensuite, tu crées une variable locale du type
correspondant. Intérêt premier : Tu es certain que la ressource sera
libérée quoi qu'il arrive.


C'est bien l'origine du nom, mais l'important n'est pas d'acquérir la
ressource dans le construteur. L'important dans l'idiome, c'est que la
ressource appartient à une classe dont le destructeur le libère, et ça,
dès que la ressource est acquise. Je considèrerais les boost::shared_ptr
et al. comme un exemple de RAII, mais la ressournce en question (la
mémoire) n'est pas acquise dans le constructeur.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2 3 4