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
kanze
Loïc Joly wrote in message
news:<cbckct$aol$...

Nicolas Aunai wrote:

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.


Certains, ou prèsque tous ?

Mais ça a des limites. Si j'écris :
vect v( a + b ) ;
ça marche, mais dans :
v = a + b ;
c'est moins sûr. Il y aurait au moins une copie.

- 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)


C'est la solution consacrée. En théorie, il faut faire vachement
attention à la durée de vie des objets, mais dans la pratique, ça marche
pas trop mal, parce que les objets intermédiaires qui dépendent des
autres objets sont tous des temporaires.

- 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.


On pourrait aussi utiliser COW, surtout si le programme n'est pas
multi-threadé.

--
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


Avatar
Nomak
Le 23/06/2004 à 20:07:20, Nicolas Aunai a
écrit:

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


Alloue ton vecteur sur la pile:

vect &
vect::operator+(const vect &v) const
{
// si on ne peut pas additionner partiellemnt
assert(x.size() == v.x.size());

// le constructeur par défaut alloue suffisemment de mémoire?
// c'est pas vect res(x.size()); ?
vect res;

// res.x = x + v.x
for (unsigned i = 0; i < x.size(); i++)
*res.x[i] = *x[i] + *v.x[i];

// copie de la pile vers l'espace automatique
// le compilo devrait optimiser ça
return res;
}

Tu peux aussi faire:

vect &
vect::operator+(const vect &v) const
{
// si on ne peut pas additionner partiellemnt
assert(x.size() == v.x.size());

vect res(*this);

// res.x += v.x
for (unsigned i = 0; i < x.size(); i++)
*res.x[i] += *v.x[i];

// copie de la pile vers l'espace automatique
// le compilo devrait optimiser ça
return res;
}


On suppose que vect::~vect libère come il faut la mémoire, et il n'y a
pas de fuites.


--
Nomak

Avatar
tib.motuelle
Nomak wrote in message news:<ot0cgwb7edmn.juz96zxhxob5$...
Le 23/06/2004 à 20:07:20, Nicolas Aunai a
écrit:

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;

}

[...]


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 ??

Alloue ton vecteur sur la pile:



Oui mais si tu ne changes pas le type de retour (l'opérateur doit
retourner un nouvel objet, pas une référence), tu as juste transformé
la fuite mémoire en comportement indéfini (qui devrait rapidement se
traduire par un plantage à l'execution).

[snip première version]

vect &
vect::operator+(const vect &v) const
{
// si on ne peut pas additionner partiellemnt
assert(x.size() == v.x.size());

vect res(*this);

// res.x += v.x
for (unsigned i = 0; i < x.size(); i++)
*res.x[i] += *v.x[i];

// copie de la pile vers l'espace automatique
// le compilo devrait optimiser ça
return res;
}
Ici "res" sort de la portée et est détruit. La référence retournée est

invalide.

En changeant le type de retour de l'opérateur en:
vect
vect::operator+(const vect &v) const

Tu retournes maintenant une copie de res (qui peut être optimisée), et
les choses rentrent dans l'ordre.

Bertrand.


Avatar
Nomak
Le 24/06/2004 à 16:27:24, Bertrand Motuelle
a écrit:

Alloue ton vecteur sur la pile:


Oui mais si tu ne changes pas le type de retour (l'opérateur doit
retourner un nouvel objet, pas une référence), tu as juste transformé
la fuite mémoire en comportement indéfini (qui devrait rapidement se
traduire par un plantage à l'execution).

[snip première version]

vect &
vect::operator+(const vect &v) const
{
// si on ne peut pas additionner partiellemnt
assert(x.size() == v.x.size());

vect res(*this);

// res.x += v.x
for (unsigned i = 0; i < x.size(); i++)
*res.x[i] += *v.x[i];

// copie de la pile vers l'espace automatique
// le compilo devrait optimiser ça
return res;
}
Ici "res" sort de la portée et est détruit. La référence retournée est

invalide.

En changeant le type de retour de l'opérateur en:
vect
vect::operator+(const vect &v) const

Tu retournes maintenant une copie de res (qui peut être optimisée), et
les choses rentrent dans l'ordre.


Tout à fait, désolé

--
Nomak


Avatar
Mathieu Roger
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



La solution que j'utilise pour ce genre de truc c'est de faire de vect
une classe de valeur, c'est-à-direune classe possédant :
-un constructeur par défaut
-un constructeur de copie
-une surcharge d'affectation
-une surcharge de == et !
Ensuite, plus de new vect mais par exemple dans + :


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

Attention pas de référence sur le résultat
------------------------------------------

le resultat est copié au return, il y a ensuite moyen de limiter le
nombre de copies en n'ayant que des pointeurs comme attributs de vect et
en utilisant un comptage de références : la copie fait pointer sur le
même et dès qu'on change le contenu alors là on fait une copie

en fait ta classe vect ets une classe de valeurs, le plus simple est de
se débrouiller pour pouvoir l'utiliser sans jamais faire appel à un new

notes qu'avec cette solution, on peut ecrire :

vect a = b + c + d ;


la suite est un petit exemple avec des attributs bidons mais qui fait le
job :

#include <iostream>
#include <stdlib.h>

using namespace std ;

// on compte les constructions et on décompte les destructions
int nombreConstruitPasDetruit = 0 ;

class vect {
public:
vect()
{
cout << "une construction n" ;
nombreConstruitPasDetruit++ ;
}

vect(const int _x, const int _y)
: x(_x), y(_y)
{
cout << "une construction n" ;
nombreConstruitPasDetruit++ ;

}


vect(const vect& _v)
: x(_v.x), y(_v.y)
{
cout << "une construction n" ;
nombreConstruitPasDetruit++ ;

}

~vect()
{
cout << "une destruction n" ;
nombreConstruitPasDetruit-- ;
}

vect& operator=(const vect& _v) {

if (&_v != this) {

this->x = _v.x ;
this->y = _v.y ;
}

return *this ;

}


vect operator+(const vect& _v) const {

vect resultat ;

resultat.x = this->x + _v.x ;
resultat.y = this->y + _v.y ;

return resultat ;

}

void print() const {

cout << "(" << this->x << "," << this->y << ")n" ;

}

private:

int x ;
int y ;


};


int main() {

{
vect a(1,4) ;
vect b(4,2) ;
vect c(4,9) ;

vect res = a + b + c ;

res.print() ;
}

// normalement ici, ya plus d'objets
cout << nombreConstruitPasDetruit << "n" ;

}

Avatar
drkm
writes:

Loïc Joly wrote in message
news:<cbckct$aol$...


[à propos 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 :



[...]

- 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)


C'est la solution consacrée.


Porte-t-elle un nom ? J'ai jeté un coup d'oeil au tiny_vector de
Blitz, mais cela est plutôt ardu pour découvrir une telle technique.
Peut-être as-tu l'une ou l'autre référence ?

Ca m'a l'air en fait assez proche du Proxy, non ?

--drkm


Avatar
Loïc Joly
drkm wrote:
Porte-t-elle un nom ? J'ai jeté un coup d'oeil au tiny_vector de
Blitz, mais cela est plutôt ardu pour découvrir une telle technique.
Peut-être as-tu l'une ou l'autre référence ?


L'excellent "C++ template, the complete guide " de Josuttis et
Vandevoorde en parle.

--
Loïc

Avatar
darkman_spam
Loïc Joly wrote in message <cc4ctg$l2f$:

drkm wrote:

Porte-t-elle un nom ? J'ai jeté un coup d'oeil au tiny_vector de
Blitz, mais cela est plutôt ardu pour découvrir une telle technique.
Peut-être as-tu l'une ou l'autre référence ?


L'excellent "C++ template, the complete guide " de Josuttis et
Vandevoorde en parle.


Merci.

Peut-être avec l'été vais-je enfin me décider à le lire. Depuis le
temps que je le souhaite ...

--drkm


Avatar
kanze
Loïc Joly wrote in message
news:<cc4ctg$l2f$...
drkm wrote:
Porte-t-elle un nom ?



À vrai dire, pas que je sache. Mais c'est un problème que je n'ai suivi
que de loin, étant donné qu'il a peu d'application pratique dans les
domaines où je travaille.

J'ai jeté un coup d'oeil au tiny_vector de Blitz, mais cela est
plutôt ardu pour découvrir une telle technique. Peut-être as-tu
l'une ou l'autre référence ?


L'excellent "C++ template, the complete guide " de Josuttis et
Vandevoorde en parle.


Ils en parlent de l'implémentation à templates. La technique est bien
plus ancienne que les templates (j'en ai entendu parler déjà en 1992, ou
quelque part par là), et marche aussi bien avec des fonctions
virtuelles. En fait, j'ai fait un benchmark il y a cinq ou six ans, avec
Sun CC 4.2, et curieusement, c'était la version avec fonctions
virtuelles qui était la plus rapide.

--
James Kanze GABI Software http://www.gabi-soft.fr
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


Avatar
drkm
writes:

Ils en parlent de l'implémentation à templates. La technique est bien
plus ancienne que les templates (j'en ai entendu parler déjà en 1992, ou
quelque part par là), et marche aussi bien avec des fonctions
virtuelles. En fait, j'ai fait un benchmark il y a cinq ou six ans, avec
Sun CC 4.2, et curieusement, c'était la version avec fonctions
virtuelles qui était la plus rapide.


Tu as deux codes équivalents, l'un utilisant des modèles, et l'autre
les mécanismes virtuels. Et le second est le plus rapide. C'est bien
cela ? Cela me paraît étrange. Te souviens-tu d'où venait la
différence de temps ?

--drkm

1 2 3 4