OVH Cloud OVH Cloud

Collection de pointeurs... Quand??

11 réponses
Avatar
Michaël Delva
Salut,

voici la petite question du soir, histoire d'être sûr de bien faire les
choses:

Quand vaut-il mieux utiliser des conteneurs de pointeurs? Quand le type
du conteneur est trop important pour une copie de ses membres? (Ex: une
classe?)

J'avoue que ce point n'est pas encore très clair pour moi...

Et en quoi différe l'utilisation d'une collection de pointeurs par
rapport à une collection de types?

Merci d'avance...

PS: je ne pense pas avoir correctement utilisé le maigre vocabulaire C++
que je connaisse, là aussi je dois progresser ;-)

10 réponses

1 2
Avatar
Benoit Rousseau
Michaël Delva wrote:
Salut,

voici la petite question du soir, histoire d'être sûr de bien faire les
choses:

Quand vaut-il mieux utiliser des conteneurs de pointeurs? Quand le type
du conteneur est trop important pour une copie de ses membres? (Ex: une
classe?)

J'avoue que ce point n'est pas encore très clair pour moi...

Et en quoi différe l'utilisation d'une collection de pointeurs par
rapport à une collection de types?

Merci d'avance...

PS: je ne pense pas avoir correctement utilisé le maigre vocabulaire C++
que je connaisse, là aussi je dois progresser ;-)


J'utilise les conteneurs de pointeurs presque à chaque fois car :
* j'utilise des objets polymorphes et des conteneurs sur la classe de base
* j'utilise des objets qui sont référencés plusieurs fois

Mais j'essaye de plus en plus d'utiliser des pointeurs intelligents
(avec comptage de références la plupart du temps)

--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Samuel Krempp
le Saturday 13 December 2003 00:52, écrivit :

Et en quoi différe l'utilisation d'une collection de pointeurs par
rapport à une collection de types?


tu veux dire une collection d'objets, j'imagine.

1/ un container va construire par copie chacun de ses éléments, ce qui peut
ne pas être possible, ou pas désirable (cas de classes polymorphes par
exple, ou aussi de très gros objets lents à copier - genre 1Ko ou plus, je
sais pas trop où on tracerait la frontière à partir d'où il est considéré
gênant de manipuler par copie)
2/ si tu choisis de mettre des pointeurs dans le container, la copie et
destruction de ces pointeurs ne fait rien sur l'objet référencé, et ce sera
à toi de gèrer la vie de tes objets alloués, de t'assurer qu'ils seront
désallouer une fois et une seule, etc.. avec des smart pointers ça peut
bien se passer (le container appelera le destructeur des smart pointers, et
ceux-ci finiront par désallouer l'objet sans intervention manuelle), mais
en gnéral c'est une source possible de complications.
aussi, l'accès aux objets référencés par les pointeurs prendra un peu plus
de temps, puisqu'il y a un niveau d'indirection supplémentaire.. (ça se
sentira lors de boucles sur l'étendue de la collection avec une instruction
très rapide, où le temps d'accès aura donc une aprt importante)


C'est en fait les différences classiques entre sémantique objet (copies
duplique l'objet) et sémantique pointeur (copie d'une référence, donc
partage de l'objet référencé, mais avec ce que ça implique de
complication).

--
Sam

Avatar
Michaël Delva
Samuel Krempp wrote in
news:3fdb073f$0$17134$:

le Saturday 13 December 2003 00:52, écrivit :

Et en quoi différe l'utilisation d'une collection de pointeurs par
rapport à une collection de types?


tu veux dire une collection d'objets, j'imagine.

1/ un container va construire par copie chacun de ses éléments, ce qui
peut ne pas être possible, ou pas désirable (cas de classes
polymorphes par exple, ou aussi de très gros objets lents à copier -
genre 1Ko ou plus, je sais pas trop où on tracerait la frontière à
partir d'où il est considéré gênant de manipuler par copie)


C'est bien ce qu'il me semblait sur ce point là...

2/ si tu choisis de mettre des pointeurs dans le container, la copie
et destruction de ces pointeurs ne fait rien sur l'objet référencé, et
ce sera à toi de gèrer la vie de tes objets alloués, de t'assurer
qu'ils seront désallouer une fois et une seule, etc.. avec des smart
pointers ça peut bien se passer (le container appelera le destructeur
des smart pointers, et ceux-ci finiront par désallouer l'objet sans
intervention manuelle), mais en gnéral c'est une source possible de
complications. aussi, l'accès aux objets référencés par les pointeurs
prendra un peu plus de temps, puisqu'il y a un niveau d'indirection
supplémentaire.. (ça se sentira lors de boucles sur l'étendue de la
collection avec une instruction très rapide, où le temps d'accès aura
donc une aprt importante)


Là par contre ça coince...

Imaginons une classe A basique, avec constructeur, destructeur, variables
et fonctions membres:

class A
{
private:
A();
~A();

int i;

void foo();
}

Si je construit un vecteur de pointeurs de A:

std::vector<A*> vect;

comment je le remplis?
comment j'utilise les fonctions de A depuis un iterateur?

tu parlais de la gestion des suppressions... Ca se passe comment dans ce
cas là?

C'est en fait les différences classiques entre sémantique objet
(copies duplique l'objet) et sémantique pointeur (copie d'une
référence, donc partage de l'objet référencé, mais avec ce que ça
implique de complication).




Avatar
Benoit Rousseau
Michaël Delva wrote:
Samuel Krempp wrote in
2/ si tu choisis de mettre des pointeurs dans le container, la copie
et destruction de ces pointeurs ne fait rien sur l'objet référencé, et
ce sera à toi de gèrer la vie de tes objets alloués, de t'assurer
qu'ils seront désallouer une fois et une seule, etc.. avec des smart
pointers ça peut bien se passer (le container appelera le destructeur
des smart pointers, et ceux-ci finiront par désallouer l'objet sans
intervention manuelle), mais en gnéral c'est une source possible de
complications. aussi, l'accès aux objets référencés par les pointeurs
prendra un peu plus de temps, puisqu'il y a un niveau d'indirection
supplémentaire.. (ça se sentira lors de boucles sur l'étendue de la
collection avec une instruction très rapide, où le temps d'accès aura
donc une aprt importante)



Là par contre ça coince...

Imaginons une classe A basique, avec constructeur, destructeur, variables
et fonctions membres:

class A
{
private:


public:
A();
~A();
void foo();


protected:
int i;
}

Si je construit un vecteur de pointeurs de A:

std::vector<A*> vect;




comment je le remplis?
vect.push_back( new A );


comment j'utilise les fonctions de A depuis un iterateur?
for( int i = 0; i < vect.size(); ++i )

vect[i]->foo();

tu parlais de la gestion des suppressions... Ca se passe comment dans ce
cas là?
supprimer tous les éléments lors de la destruction...

for( int i = 0; i < vect.size(); ++i )
delete vect[i];

Ca se manipule comme des pointeurs quoi...


--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/


Avatar
Michaël Delva
Benoit Rousseau wrote in
news:3fdb2546$0$2867$:

class A
{
private:


public:
A();
~A();
void foo();


protected:
int i;
}




Effectivement, ça marche mieux comme ça ;-)

Si je construit un vecteur de pointeurs de A:

std::vector<A*> vect;




comment je le remplis?
vect.push_back( new A );


comment j'utilise les fonctions de A depuis un iterateur?
for( int i = 0; i < vect.size(); ++i )

vect[i]->foo();

tu parlais de la gestion des suppressions... Ca se passe comment dans
ce cas là?
supprimer tous les éléments lors de la destruction...

for( int i = 0; i < vect.size(); ++i )
delete vect[i];

Ca se manipule comme des pointeurs quoi...


--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/



OK, merci beaucoup...

Si j'ai bien compris, cela me permettra d'utiliser moins de mémoire, mais
sera plus long dans les accès aux classes, c'est bien ça?

Sinon, avec les smart_pointers (c'est bien dans Boost ça?), ça se passe
comment??


Avatar
Benoit Rousseau
OK, merci beaucoup...

Si j'ai bien compris, cela me permettra d'utiliser moins de mémoire, mais
sera plus long dans les accès aux classes, c'est bien ça?
Non, tu n'utilises pas moins de mémoire avec les pointeurs... tu en

utilises un tout petit peu plus... mais c'est dérisoire...
Et ce n'est pas forcement plus long pour les accès... La différence,
c'est que la mémoire ne se trouve pas au même endroit.

Sinon, avec les smart_pointers (c'est bien dans Boost ça?), ça se passe
comment??
Il y a plein de cas différents... boost propose 5 types de smart ptr

différents (http://www.boost.org/libs/smart_ptr/index.htm peut etre plus
que 5)
Tu peux avoir un smart_ptr qui compte les références et qui efface
l'objet quand plus rien ne pointe dessus. D'autres qui se refilent le
pointeur lors d'opérations d'affectation... Ca dépend de ce que tu veux
faire.
La plupart du temps, j'utilise une classe faite-maison, à la main,
rapidement, sans trop de verification de types...
Ils sont bien expliqués dans Modern C++ design (chap 7) et More
Effective C++ (je crois)

--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Fabien LE LEZ
On Sat, 13 Dec 2003 13:34:06 +0100, Samuel Krempp
wrote:

1/ un container va construire par copie chacun de ses éléments, ce qui peut
ne pas être possible, ou pas désirable (cas de classes polymorphes par
exple, ou aussi de très gros objets lents à copier - genre 1Ko ou plus, je
sais pas trop où on tracerait la frontière à partir d'où il est considéré
gênant de manipuler par copie)


Au moment où le profiler commence à râler.
Si un objet a une sémantique de valeur (et a priori pas de fonctions
virtuelles), j'utilise d'abord la solution la plus simple, i.e.
insertion dans le conteneur par copie, quelle que soit la taille de
l'objet (que je ne connais pas forcément, d'ailleurs). Si à
l'exécution, ça prend trop de temps ou trop de RAM, je change.

--
;-)

Avatar
Samuel Krempp
le Wednesday 17 December 2003 13:53, écrivit :
Au moment où le profiler commence à râler.


mais ça prend du temps de lui permettre de râler - ou de voir qu'il râle.
(si le cteur de copie a été inliné par exple, ce n'est pas immédiat)

Si un objet a une sémantique de valeur (et a priori pas de fonctions
virtuelles), j'utilise d'abord la solution la plus simple, i.e.
insertion dans le conteneur par copie, quelle que soit la taille de
l'objet (que je ne connais pas forcément, d'ailleurs). Si à
l'exécution, ça prend trop de temps ou trop de RAM, je change.


oui, enfin, il n'est qd même pas interdit de décider au départ qu'on va
manipuler telle classe par sémantique pointeur. genre des images. Il est
fort possible que le prog puisse être lancé sur des images 32x32 qui
seraient copiées sans aucun impact, mais le cas contraire est aussi
possible, et il est raisonnable de tout de suite opter pour une sémantique
de pointeur, pour éviter d'introduire une dépendance de la complexité du
prog sur la taille de l'image inutilement à certains endroits du code.
(ça dépend de ce que fait le prog, c'est sûr..)

On peut aussi, de manière générale, miser sur le fait qu'une 'optimisation'
possible n'aurait de toute façon au pire pas d'impact négatif sur les
perfs, et perdrait moins de temps à coder directement que d'obtenir
confirmation par le profiler. Je ne sais pas si c'est dû à un mauvais
profiler, mais chez moi le choix inlinée/pas inlinée pour chaque fonction
un peu cruciale (et autres choix lourds de conséquences) mène à un grand
nombre (2^N) de profiles éventuellement très différents à analyser.. à
moins d'intuition (mais c'est l'écueil qu'on veut éviter avec le profiling)
enfin bref parfois ça peut prendre du temps de profiler.
(d'autant qu'il faut éventuellement trouver des conditions d'éxécutions
réfléchissant bien le contexte final d'utilisation ! - ce qui est
d'ailleurs forcément un peu impossible qd on écrit une bibli)

Ceci dit il est bien possible aussi qu'il y ait des trucs à savoir pour
profiler bien et vite, et que j'ignore..
--
Sam

Avatar
Fabien LE LEZ
On Thu, 18 Dec 2003 00:29:14 +0100, Samuel Krempp
wrote:

oui, enfin, il n'est qd même pas interdit de décider au départ qu'on va
manipuler telle classe par sémantique pointeur. genre des images.


A condition de s'être assuré avant que c'est vraiment utile -- si la
classe "Image" contient un système style COW, avoir quelques
temporaires en plus ou en moins ne change pas grand chose.

--
;-)

Avatar
kanze
Samuel Krempp wrote in message
news:<3fe0e6cb$0$17118$...
le Wednesday 17 December 2003 13:53, écrivit :

Au moment où le profiler commence à râler.


mais ça prend du temps de lui permettre de râler - ou de voir qu'il
râle. (si le cteur de copie a été inliné par exple, ce n'est pas
immédiat)


Des deux choses une : si ton programme tourne assez vite, tu ne vas même
pas mettre en marche le profiler ; sinon, il faut que tu fasses quelque
chose, de toute façon, et je ne connais rien de plus efficace que le
profiler.

Quant à l'histoire de inline, je ne comprends pas trop. C'est une
optimisation. Donc, avant d'avoir utiliser le profiler, tu n'as pas de
fonctions inline.

Si un objet a une sémantique de valeur (et a priori pas de fonctions
virtuelles), j'utilise d'abord la solution la plus simple, i.e.
insertion dans le conteneur par copie, quelle que soit la taille de
l'objet (que je ne connais pas forcément, d'ailleurs). Si à
l'exécution, ça prend trop de temps ou trop de RAM, je change.


oui, enfin, il n'est qd même pas interdit de décider au départ qu'on
va manipuler telle classe par sémantique pointeur. genre des images.


Certainement. D'abord, parce qu'il y a beaucoup de classes qui ne
supportent même pas la copie. Et puis, il y a des classes dont on veut
en général éviter des copies inutiles -- typiquement à cause de leur
taille, même plus que pour des raisons de performances. Des images, des
fonts, etc. en font partie. (Mais typiquement, aussi, de telles classes
ont une « identité » -- je veux pouvoir faire des modifications dans la
classe, par exemple, sans en générer une nouvelle instance que
j'affecte par la suite. Personnellement, je vois mal une classe d'image
qui supporte l'affectation, par exemple.)

Il est fort possible que le prog puisse être lancé sur des images
32x32 qui seraient copiées sans aucun impact, mais le cas contraire
est aussi possible, et il est raisonnable de tout de suite opter pour
une sémantique de pointeur, pour éviter d'introduire une dépendance de
la complexité du prog sur la taille de l'image inutilement à certains
endroits du code. (ça dépend de ce que fait le prog, c'est sûr..)

On peut aussi, de manière générale, miser sur le fait qu'une
'optimisation' possible n'aurait de toute façon au pire pas d'impact
négatif sur les perfs, et perdrait moins de temps à coder directement
que d'obtenir confirmation par le profiler.


On ne peut pas compter sur le fait que les manoeuvres nécessaires pour
éviter la copie prend moins de temps que la copie. Remplace des int par
des boost::shared_ptr<int> dans un programme, et je suis près à parier
que la plupart du temps, on ralentit le programme. Même si on a éviter
quelques copies.

Je ne sais pas si c'est dû à un mauvais profiler, mais chez moi le
choix inlinée/pas inlinée pour chaque fonction un peu cruciale (et
autres choix lourds de conséquences) mène à un grand nombre (2^N) de
profiles éventuellement très différents à analyser.. à moins
d'intuition (mais c'est l'écueil qu'on veut éviter avec le profiling)


Le premier profiling a lieu avant le moindre inline. Il nous dit ce
qu'il faut mettre en inline (ou autre chose -- l'inline n'est pas la
réponse à tout).

enfin bref parfois ça peut prendre du temps de profiler. (d'autant
qu'il faut éventuellement trouver des conditions d'éxécutions
réfléchissant bien le contexte final d'utilisation ! - ce qui est
d'ailleurs forcément un peu impossible qd on écrit une bibli)


Les bibliothèques posent effectivement des problèmes à cet égard ; si on
écrit une bibliothèque standard, par exemple, on n'a probablement pas
accès aux profilings des utilisateurs finaux, et il faut déviner un peu.
Donc, il y a de fortes chances que vector<>::operator[] doit être
inline, tandis que c'est prèsque certain que ça ne vaut pas la peine en
ce qui concerne les fonctions de formattage des sorties.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16


1 2