Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tableaux et stl

5 views
Skip to first unread message

JM

unread,
Sep 18, 2006, 11:53:27 AM9/18/06
to
Bonjour

Toujours plongé dans la stl, j'ai quelques questions (Pas évident de
trouver un tutoriel à la fois clair et compréhensible)

0) Où peut-on trouver un bon tutoriel (Français de préférence, mais à
défaut, l'anglais me conviendra)

1)
J'ai une classe :
class CA
{public
CA() { initialisations divers et variées; }
~CA();

void f();
}

vector<CA> tableau;

tableau.size(10);

Le constructeur est-il bien appelé pour les 10 éléments?
Je pense que oui mais le doute est en moi.

2) Toujours avec la classe précédente.

Je veux ajouter un élément à la fin de mon tableau.
A priori, tableau.push_back ne peut-être utilisé sans passer de référence.

J'ai essayé deux trucs :
a) tableau.push_back(CA());

Vu que cela crée un objet temporaire sur la pile, cela ne me
convient pas trop.

b) tableau.resize(tableau.size()+1)

Cela me parait un peu bourrin, mais c'est le seul truc que j'ai pu
trouver dans les tutoriels que j'ai vus.


y'a-t-il d'autres possibilités, sachant que toutes les initialisations
dont j'ai besoin sont faites dans le constructeur?

3) Jusqu'à présent, style C oblige, j'utilisais toujours une variable
pour conserver la taille de mon tableau.

Je suppose que cette manière de faire est obsolète avec size() ?

Merci d'avance à ceux qui répondront à ces questions certainement niveau
0, mais j'avoue que cela m'aidera bien!

Fabien LE LEZ

unread,
Sep 18, 2006, 12:21:10 PM9/18/06
to
On Mon, 18 Sep 2006 17:53:27 +0200, JM <nosp...@yahoo.com>:

>tableau.size(10);

vector<>::size() ne prend pas d'argument, et renvoie le nombre actuel
d'éléments.

Tu voulais peut-être écrire .resize(10) ?

>Le constructeur est-il bien appelé pour les 10 éléments?

Oui.

>A priori, tableau.push_back ne peut-être utilisé sans passer de référence.

Disons que si tu veux ajouter un élément, il faut lui indiquer quoi
ajouter, effectivement.

>J'ai essayé deux trucs :
>a) tableau.push_back(CA());
>
> Vu que cela crée un objet temporaire sur la pile, cela ne me
>convient pas trop.

Pourquoi ? Quel est le problème ?

>b) tableau.resize(tableau.size()+1)
>
> Cela me parait un peu bourrin,

Effectivement, c'est pas terrible :-(


>3) Jusqu'à présent, style C oblige, j'utilisais toujours une variable
>pour conserver la taille de mon tableau.
>
>Je suppose que cette manière de faire est obsolète avec size() ?

Effectivement.

Sylvain

unread,
Sep 18, 2006, 12:46:24 PM9/18/06
to
Fabien LE LEZ wrote on 18/09/2006 18:21:
>
>> A priori, tableau.push_back ne peut-être utilisé sans passer de référence.
>
> Disons que si tu veux ajouter un élément, il faut lui indiquer quoi
> ajouter, effectivement.

je n'ai pas lu qu'il souhaitais ajouter "rien", mais était géné
d'ajouter une *référence* (et non un pointeur).

>> J'ai essayé deux trucs :
>> a) tableau.push_back(CA());
>>
>> Vu que cela crée un objet temporaire sur la pile, cela ne me
>> convient pas trop.
>
> Pourquoi ? Quel est le problème ?

ben parce que "cela créé un temporaire" !

- on peux vouloir la "classe vectorisée" non copiable.
- on peux, en effet, trouver étonnant le fait de transmettre un truc ne
servant qu'à être jeté (création tempo, instantiation ptr, recopie,
destruction; tout ça à la place d'une simple affectation de pointeur ne
parait pas très vertueux).

Sylvain.

JM

unread,
Sep 18, 2006, 12:49:25 PM9/18/06
to

Fabien LE LEZ a écrit :

> Tu voulais peut-être écrire .resize(10) ?

Tout à fait!

>
>> Le constructeur est-il bien appelé pour les 10 éléments?
>
> Oui.

Ok, ça paraissait logique, mais bon.

>> A priori, tableau.push_back ne peut-être utilisé sans passer de référence.
>
> Disons que si tu veux ajouter un élément, il faut lui indiquer quoi
> ajouter, effectivement.

Disons que cela me facilitait la tache.
Il va juste falloir que je redéfinisse mon constructeur, c'est pas trop
gênant.

>> Vu que cela crée un objet temporaire sur la pile, cela ne me
>> convient pas trop.
>
> Pourquoi ? Quel est le problème ?

Quand c'est une instruction très fréquente, cela doit être plutôt
pénalisant, non?

>> 3) Jusqu'à présent, style C oblige, j'utilisais toujours une variable
>> pour conserver la taille de mon tableau.
>>
>> Je suppose que cette manière de faire est obsolète avec size() ?
>
> Effectivement.

Ok, merci.

J'ai l'impression que les STL facilitent bien la vie, mais cela implique
de prendre de nouvelles habitudes :o)

JM

unread,
Sep 18, 2006, 1:00:47 PM9/18/06
to

Sylvain a écrit :

> ben parce que "cela créé un temporaire" !
>
> - on peux vouloir la "classe vectorisée" non copiable.
> - on peux, en effet, trouver étonnant le fait de transmettre un truc ne
> servant qu'à être jeté (création tempo, instantiation ptr, recopie,
> destruction; tout ça à la place d'une simple affectation de pointeur ne
> parait pas très vertueux).


Tout à fait
J'ai résolu le problème en changeant mon constructeur.
C'est finalement devenu plus propre et plus clair.

Jean-Marc Desperrier

unread,
Sep 18, 2006, 1:01:34 PM9/18/06
to
JM wrote:
> Je veux ajouter un élément à la fin de mon tableau.
> A priori, tableau.push_back ne peut-être utilisé sans passer de référence.
>
> J'ai essayé deux trucs :
> a) tableau.push_back(CA());
>
> Vu que cela crée un objet temporaire sur la pile, cela ne me convient
> pas trop.
>
> b) tableau.resize(tableau.size()+1)
>
> Cela me parait un peu bourrin, mais c'est le seul truc que j'ai pu
> trouver dans les tutoriels que j'ai vus.
>
> y'a-t-il d'autres possibilités, sachant que toutes les initialisations
> dont j'ai besoin sont faites dans le constructeur?

Si tu as un problème de ce type, il me semble que c'est a priori que tu
ferais mieux d'éviter std::vector et d'utiliser plutôt une std::list :
tu sous-entends qu'il est ici lourd et à éviter que de créer un
temporaire pour copie. Or le vecteur fait cela tout le temps dès que la
taille change, et le list permet de l'éviter.

La contre-partie est de perdre l'accès en temps constant à n'importe
quel élément, mais il faut juste voir lequel des deux est le plus important.

Si l'accès en temps constant est vraiment important ensuite, on peut
aussi décider de calculer une borne supérieure au nombre d'éléments
nécessaires, de faire un v.reserve, puis d'utiliser v.resize(v.size()+1)
et v.back().

Ou regarder si ce n'est pas le std::deque qui serait le meilleur compromis.

kanze

unread,
Sep 18, 2006, 1:12:24 PM9/18/06
to
Sylvain wrote:
> Fabien LE LEZ wrote on 18/09/2006 18:21:

> >> A priori, tableau.push_back ne peut-être utilisé sans
> >> passer de référence.

> > Disons que si tu veux ajouter un élément, il faut lui indiquer quoi
> > ajouter, effectivement.

> je n'ai pas lu qu'il souhaitais ajouter "rien", mais était
> géné d'ajouter une *référence* (et non un pointeur).

> >> J'ai essayé deux trucs :
> >> a) tableau.push_back(CA());

> >> Vu que cela crée un objet temporaire sur la pile, cela ne me
> >> convient pas trop.

> > Pourquoi ? Quel est le problème ?

> ben parce que "cela créé un temporaire" !

C'est un peu la philosophie de base de C++. On a des temporaires
sur la pile, plutôt que seulement des objets créés
dynamiquement.

> - on peux vouloir la "classe vectorisée" non copiable.

On ne peut pas. C'est contre la philosophie de base. (En fait,
je vois mal comment ça serait implémentable.)

> - on peux, en effet, trouver étonnant le fait de transmettre
> un truc ne servant qu'à être jeté (création tempo,
> instantiation ptr, recopie, destruction; tout ça à la place
> d'une simple affectation de pointeur ne parait pas très
> vertueux).

Et quelle est l'alternative ? Crée tout dynamiquement, et ne
travailler qu'avec des pointeurs ?

À la fin, il faut comprendre le langage. Il permet bien les deux
modèles ; la sémantique de valeur, c'est le défaut, mais il a
tout ce qu'il faut pour implémenter la sémantique de référence
quand on en a besoin.

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

kanze

unread,
Sep 18, 2006, 1:15:47 PM9/18/06
to
JM wrote:
> Fabien LE LEZ a écrit :

> >> A priori, tableau.push_back ne peut-être utilisé sans
> >> passer de référence.

> > Disons que si tu veux ajouter un élément, il faut lui
> > indiquer quoi ajouter, effectivement.

> Disons que cela me facilitait la tache. Il va juste falloir
> que je redéfinisse mon constructeur, c'est pas trop gênant.

Il faut bien définir la sémantique de ta classe. Et si elle n'a
pas une sémantique de valeur (avec la copie, etc.), on ne peut
pas le mettre dans un tableau. Il existe alors d'autres
solutiones, des tableaux de pointeurs, etc.

> >> Vu que cela crée un objet temporaire sur la pile, cela
> >> ne me convient pas trop.

> > Pourquoi ? Quel est le problème ?

> Quand c'est une instruction très fréquente, cela doit être
> plutôt pénalisant, non?

Par rapport à quoi ?

kanze

unread,
Sep 18, 2006, 1:18:03 PM9/18/06
to
Fabien LE LEZ wrote:
> On Mon, 18 Sep 2006 17:53:27 +0200, JM <nosp...@yahoo.com>:

> >b) tableau.resize(tableau.size()+1)

> > Cela me parait un peu bourrin,

> Effectivement, c'est pas terrible :-(

Note aussi en plus que ça risque de créer un élément temporaire
quelque part aussi, qu'on copie.

Alain Gaillard

unread,
Sep 18, 2006, 1:39:48 PM9/18/06
to
Sylvain a écrit :


> - on peux vouloir la "classe vectorisée" non copiable.
> - on peux, en effet, trouver étonnant le fait de transmettre un truc ne
> servant qu'à être jeté (création tempo, instantiation ptr, recopie,
> destruction; tout ça à la place d'une simple affectation de pointeur ne
> parait pas très vertueux).

Comment ça, simple affectation de pointeur ?

--
Alain

Sylvain

unread,
Sep 18, 2006, 3:01:26 PM9/18/06
to
kanze wrote on 18/09/2006 19:12:
>
>> ben parce que "cela créé un temporaire" !
>
> C'est un peu la philosophie de base de C++. On a des temporaires
> sur la pile, plutôt que seulement des objets créés
> dynamiquement.

j'aime cette philosophie, la coexistence de variables statiques ET
dynamiques me va très bien également; non, je ne vois pas comment
efficacement sans passer.

c'est ici seulement l'interface de vector<> dont il est question.

>> - on peux vouloir la "classe vectorisée" non copiable.
>
> On ne peut pas. C'est contre la philosophie de base. (En fait,
> je vois mal comment ça serait implémentable.)

je suis également d'accord avec les exigences du contrat de vector<>, je
soulignais le trait pour indiquer qu'il a *IMHO* un caractère négatif.

il reste toutefois des cas où on a bien affaire à des instances qui
doivent être non copiables (wrapper de resources physiques par exemple,
ou gestionnaire d'accès à une source non multi-entrante, etc); de tels
gestionnaires ne peuvent pas être regroupés / utilisés via un vector<>.

>> - on peux, en effet, trouver étonnant le fait de transmettre
>> un truc ne servant qu'à être jeté (création tempo,
>> instantiation ptr, recopie, destruction; tout ça à la place
>> d'une simple affectation de pointeur ne parait pas très
>> vertueux).
>
> Et quelle est l'alternative ? Crée tout dynamiquement, et ne
> travailler qu'avec des pointeurs ?

une alternative simple serait de pouvoir transmettre un T* (et même tout
pointeur sur une classe assimilable à un T (T et ses sous classes)).

l'organisation interne de vector<> passe nécessairement par des
pointeurs de la classe template, n'imposer que des références à
l'insertion ne me parait pas si indispensable. (ok, tu vas me répondre
que je n'ai pas le droit de lire la définition du template ou d'en tirer
des conclusions).

si la question était ouverte ("si" parce que heureusement 99% des
utilisateurs se foutent de l'avis qui suit), le mérite de vector<> est
son mécanisme allocator customisable (justement pour traiter les
références) et son redimensionnement, dans un second temps ses itérateurs.

les 2 premiers points garantissent de travailler avec des références
(T&), c'est comme cela que je travaille dès que possible donc il ne
s'agirait pas de "travailler avec des pointeurs", surtout pas; par
contre je ne trouverais pas gênant de faire un push(new T(...)).

> À la fin, il faut comprendre le langage. Il permet bien les deux
> modèles ; la sémantique de valeur, c'est le défaut, mais il a
> tout ce qu'il faut pour implémenter la sémantique de référence
> quand on en a besoin.

je sais ce qu'est un tempo vs une référence, la question n'était pas là
il me semble.

Sylvain.

JM

unread,
Sep 18, 2006, 5:01:15 PM9/18/06
to

Jean-Marc Desperrier a écrit :

> Si tu as un problème de ce type, il me semble que c'est a priori que tu
> ferais mieux d'éviter std::vector et d'utiliser plutôt une std::list :
> tu sous-entends qu'il est ici lourd et à éviter que de créer un
> temporaire pour copie. Or le vecteur fait cela tout le temps dès que la
> taille change, et le list permet de l'éviter.

Si on fait un resize avec une taille inférieure, il y a réallocation, ou
bien est-ce que cela garde la zone mémoire actuelle?

Sylvain

unread,
Sep 18, 2006, 6:20:19 PM9/18/06
to
Alain Gaillard wrote on 18/09/2006 19:39:
>
>> (std::vector<>)

>
> Comment ça, simple affectation de pointeur ?

parce que vector<> est resizable, il repose nécessairement sur un array
de pointeur sur la classe template. il ne s'agit pas d'hypothèses sur
une implémentation X ou Y de telle STL mais d'une nécessité.

dès lors le moyen d'insérer un nouvel item dans un vector<> (pas de
créer le vector initial avec une taille non nulle) est:

- de recevoir une référence ou un temporaire (la différence n'existant
que dans le contexte de l'appelant) pour en réaliser une copie dynamique
et stocker cette copie.

- de recevoir un pointeur sur une instance dynamique, dans ce cas ce
pointeur pourrait être directement affecté au nouvel emplacement créé
dans le vector ou irait en remplacement d'un item existant (idiome
setAt(..)).

si un objectif important des rédacteurs de vector<> est d'éviter toute
erreur sur les pointeurs, leur choix fait sens, on ne pourra pas écrire
par exemple:

T* t = new T(...);
v.push(t);
delete t;

qui planterait; son interface impose:

T* t = new T(...);
v.push(*t);
delete t;

qui, parce que "t" a été cloné, ne plantera pas.

ce qui me ""gène"" est le fait que cette interface résulte (un peu) de
choix non sémantiques et/ou rationnelles mais sur des hypothèses sur le
codeur distrait - soit, ils existent; soit le langage peut aider à
limiter la casse, ...

soit aussi, il "suffit" de créer un vector<T*> (et non vector<T>) si
l'on souhaite insérer des T*, mais je reboucle alors sur la réponse
faite à James, on peux souhaiter contrôler / réaliser l'instantiation
des items mais manipuler des références (et non des pointeurs) lors de
l'utilisation des opérateurs [] de vector<> et de ses itérateurs.

Sylvain.

Sylvain.

Sylvain

unread,
Sep 18, 2006, 6:56:40 PM9/18/06
to
JM wrote on 18/09/2006 23:01:
>
> Si on fait un resize avec une taille inférieure, il y a réallocation, ou
> bien est-ce que cela garde la zone mémoire actuelle?

cela peut dépendre de l'implémentation, je pense que la réallocation
n'est pas garantie: le vector<> travaillant naturellement avec son
concept d'itérateur, il aura tendance à simplement ajuster ses bornes.

évidemment un resize(10% * size()) pose dès lors en problème en
l'absence d'une méthode pack() (compactant le vecteur a sa seule taille
nécessaire).

Sylvain.

Loïc Joly

unread,
Sep 18, 2006, 7:15:31 PM9/18/06
to
Sylvain a écrit :

> soit aussi, il "suffit" de créer un vector<T*> (et non vector<T>) si
> l'on souhaite insérer des T*, mais je reboucle alors sur la réponse
> faite à James, on peux souhaiter contrôler / réaliser l'instantiation
> des items mais manipuler des références (et non des pointeurs) lors de
> l'utilisation des opérateurs [] de vector<> et de ses itérateurs.

J'ai un peu l'impression que ce que tu souhaites, c'est un adaptateur au
dessus de vector permettant de manipuler un vector<T*> comme s'il gérait
des T.

As-tu regardé boost::iterator, en particulier
http://www.boost.org/libs/iterator/doc/indirect_iterator.html ?

--
Loïc

kanze

unread,
Sep 19, 2006, 3:14:29 AM9/19/06
to
Sylvain wrote:
> kanze wrote on 18/09/2006 19:12:

> >> ben parce que "cela créé un temporaire" !

> > C'est un peu la philosophie de base de C++. On a des
> > temporaires sur la pile, plutôt que seulement des objets
> > créés dynamiquement.

> j'aime cette philosophie, la coexistence de variables statiques ET
> dynamiques me va très bien également; non, je ne vois pas comment
> efficacement sans passer.

> c'est ici seulement l'interface de vector<> dont il est question.

Certes. Tout ce que je disais, c'est qu'elle est conforme avec
la reste du C++.

> >> - on peux vouloir la "classe vectorisée" non copiable.

> > On ne peut pas. C'est contre la philosophie de base. (En fait,
> > je vois mal comment ça serait implémentable.)

> je suis également d'accord avec les exigences du contrat de
> vector<>, je soulignais le trait pour indiquer qu'il a *IMHO*
> un caractère négatif.

C'est là où je ne suis pas. Pour dire qu'il a un caractère
négatif, il faut bien présenter une alternative qui ne l'a pas.
Or, je crois que cette caractèristique est implicite dans
l'orientation valeur ; tu ne peux pas faire une collection à
orientation valeur sans l'avoir.

On peut discuter sur les convenances des choix de base ; selon
l'application et le style de programmation, différents choix
pourraient avoir des avantages. Mais je vois mal, une fois le
choix de base fait pour la sémantique de valeur, comment se
passer des temporaires et des copies. J'ai plutôt l'impression
qu'ils sont implicits dans le choix de base.

> il reste toutefois des cas où on a bien affaire à des
> instances qui doivent être non copiables (wrapper de resources
> physiques par exemple, ou gestionnaire d'accès à une source
> non multi-entrante, etc); de tels gestionnaires ne peuvent pas
> être regroupés / utilisés via un vector<>.

Tout à fait d'accord. Aucun choix de base ne convient pour tout,
et il s'agit ici des éléments pour lesquels le choix de base
sémantique de valeur ne convient.

Le C++ a fait un choix, dès le départ, de se baser sur une
sémantique de valeur, avec des pointeurs et l'allocation
dynamique pour implémenter d'autres sémantiques dans les cas où
elles conviennent. Des classes de la STL suivent ce choix, et on
est bien amené dans certains cas de faire des collections des
pointeurs, de même que si je veux renvoyer un tel objet d'une
fonction, je me sers d'un pointeur.

> >> - on peux, en effet, trouver étonnant le fait de transmettre
> >> un truc ne servant qu'à être jeté (création tempo,
> >> instantiation ptr, recopie, destruction; tout ça à la place
> >> d'une simple affectation de pointeur ne parait pas très
> >> vertueux).

> > Et quelle est l'alternative ? Crée tout dynamiquement, et ne
> > travailler qu'avec des pointeurs ?

> une alternative simple serait de pouvoir transmettre un T* (et
> même tout pointeur sur une classe assimilable à un T (T et ses
> sous classes)).

Chose que rien ne t'empèche à faire. std::vector<T*> marche très
bien.

Évidemment, dans de tels cas, une allocation dynamique avec une
gestion explicite de la durée de vie de l'objet (et de la
mémoire, si tu n'utilises pas de glaneur de cellules) s'impose.

> l'organisation interne de vector<> passe nécessairement par
> des pointeurs de la classe template, n'imposer que des
> références à l'insertion ne me parait pas si indispensable.
> (ok, tu vas me répondre que je n'ai pas le droit de lire la
> définition du template ou d'en tirer des conclusions).

Non, mais je n'y vois pas tellement d'utilisation des pointeurs
à des T. Il n'y a certainement pas un pointeur par élément.

Je ne suis pas sûr de t'avoir bien compris, d'ailleurs. Dans la
pratique, quand il s'agit d'un paramètre d'une fonction, une
référence et un pointeur sont plus ou moins la même chose. À
part la syntaxe, la seule véritable différence, c'est que tu
peux passer un temporaire à une référence à const, mais non à un
pointeur à const. La question intéressante me semble bien plus,
est-ce que le vector stocke une copie de l'objet qu'on lui
fournit, ou est-ce qu'il stocke l'objet même ? S'il stocke une
copie, il faut bien que l'objet supporte la copie, et
évidemment, il y aurait des copies chaque fois qu'on insère un
objet, et je vois mal comment on pourrait éviter la construction
du temporaire, puis sa copie. Si on stocke l'objet même, on
évite la copie, mais la gestion de la durée de vie de l'objet
reste à charge de l'utilisateur -- dans la pratique, ça veut
dire que l'objet doit être alloué dynamiquement la plupart du
temps.

Dans les fait, la STL supporte les deux. La copie, tu l'as par
défaut, avec std::vector<T>. Sinon, tu fais un std::vector<T*>,
et tu as l'autre sémantique.

> si la question était ouverte ("si" parce que heureusement 99%
> des utilisateurs se foutent de l'avis qui suit), le mérite de
> vector<> est son mécanisme allocator customisable (justement
> pour traiter les références) et son redimensionnement, dans un
> second temps ses itérateurs.

Je ne vois pas trop le rapport entre l'allocator et les
références ici. Je sais que l'allocator déclare le type
reference, mais je crois que par ailleurs, il y a assez de
contraints sur ce type pour qu'il ne peut être qu'une vraie
référence. Historiquement, Stepanov a conçu des allocator pour
gérer un peu les modèles de compilation des Intel 16 bits : on
pouvait avoir un vector avec plus de 64 Ko même en compilant
avec modèle small, à condition d'utiliser un allocator où
reference et pointer avaient des attributes FAR. Quelque part
pendant la normalisation, cette motivation a disparue, mais le
typedef pour reference a resté ; dans la pratique, si ce n'est
pas une vraie référence C++, il doit être un type d'extension
fourni le compilateur et qui se comporte à tout égards comme une
vrai référence. Aujourd'hui, sur un système généraliste (Windows
ou Unix), à peu près la seule utilisation des allocator sur
mesure que je connais, c'est de mettre une collection dans la
mémoire partagée.

> les 2 premiers points garantissent de travailler avec des
> références (T&), c'est comme cela que je travaille dès que
> possible donc il ne s'agirait pas de "travailler avec des
> pointeurs", surtout pas; par contre je ne trouverais pas
> gênant de faire un push(new T(...)).

Chose qui marche parfaitement bien avec un std::vector<T*>. Ce
que tu gagnes d'une côté (pas de copie), tu perds de l'autre
(allocation dynamique, nécessité d'une gestion explicite de la
durée de vie de l'objet). Parfois, c'est un qui convient,
parfois l'autre.

Jean-Marc Bourguet

unread,
Sep 19, 2006, 4:31:07 AM9/19/06
to
Sylvain <noS...@mail.net> writes:

> Alain Gaillard wrote on 18/09/2006 19:39:
>>
>>> (std::vector<>)
>> Comment ça, simple affectation de pointeur ?
>
> parce que vector<> est resizable, il repose nécessairement sur un array
> de pointeur sur la classe template. il ne s'agit pas d'hypothèses sur une
> implémentation X ou Y de telle STL mais d'une nécessité.

Les std::vector sont nécessairement stockés de façon contigue:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#69
Ça fait partie des précisions publiées dans C++2003.

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

kanze

unread,
Sep 19, 2006, 5:32:46 AM9/19/06
to
Sylvain wrote:
> Alain Gaillard wrote on 18/09/2006 19:39:

> >> (std::vector<>)

> > Comment ça, simple affectation de pointeur ?

> parce que vector<> est resizable, il repose nécessairement sur
> un array de pointeur sur la classe template. il ne s'agit pas
> d'hypothèses sur une implémentation X ou Y de telle STL mais
> d'une nécessité.

Sauf que je ne connais aucune implémentation qui marche comme
ça. C'est même intérdite par la norme (version 2003).

> dès lors le moyen d'insérer un nouvel item dans un vector<>
> (pas de créer le vector initial avec une taille non nulle)
> est:

> - de recevoir une référence ou un temporaire (la différence
> n'existant que dans le contexte de l'appelant) pour en
> réaliser une copie dynamique et stocker cette copie.

Sauf que la mémoire où est fait cette copie dynamique vient de
std::vector lui-même. Ultérieurement, oui, c'est de la mémoire
dynamique, mais (dans std::vector, au moins), c'est un seul bloc
de mémoire dynamique pour tous les éléments (ce qui est même
exigé de la norme actuelle, de façon à ce que tu peux utiliser
&v[0] comme paramètre à une fonction ancienne qui s'attend à un
T[], converti en pointeur).

[...]


> ce qui me ""gène"" est le fait que cette interface résulte (un
> peu) de choix non sémantiques et/ou rationnelles mais sur des
> hypothèses sur le codeur distrait - soit, ils existent; soit
> le langage peut aider à limiter la casse, ...

Stepanov n'a jamais pris en compte le « codeur distrait ».
Ici, il a simplement été cohérent avec la philosophie de C++,
qui utilise une sémantique de valeur par défaut.

kanze

unread,
Sep 19, 2006, 5:49:43 AM9/19/06
to

La seule fonction qui peut réduire la taille du bloc de mémoire
allouée, c'est swap. Les contraites sur la complexité et la
validité des itérateurs, et les restrinctions sur les exceptions
font qu'aucune autre fonction qui reduit la taille du vector ne
peut pas déplacer le bloc de mémoire, ni même réduire la
capacité du vecteur.

kanze

unread,
Sep 19, 2006, 5:58:34 AM9/19/06
to
Sylvain wrote:
> JM wrote on 18/09/2006 23:01:

> > Si on fait un resize avec une taille inférieure, il y a
> > réallocation, ou bien est-ce que cela garde la zone mémoire
> > actuelle?

> cela peut dépendre de l'implémentation,

Pas vraiment. La norme spécifie beaucoup en ce qui concerne
std::vector, et ces spécifications ont dans la pratique l'effet
d'imposer une seule implémentation.

> je pense que la réallocation n'est pas garantie:

C'est même garantie qu'elle n'aurait pas lieu.

> le vector<> travaillant naturellement avec son concept
> d'itérateur, il aura tendance à simplement ajuster ses bornes.

> évidemment un resize(10% * size()) pose dès lors en problème
> en l'absence d'une méthode pack() (compactant le vecteur a sa
> seule taille nécessaire).

Quel problème ? Dans beaucoup d'utilisations, il fait
exactement ce qu'on veut :

std::vector< int > v ;
while ( ... ) {
v.clear() ;
// un packet de v.push_back()
// puis le traitement...
}

Ici, v atteindra rapidement sa taille maximum, et il n'y aura
plus d'allocations dynamique. Si ce n'est pas ce que je veux,
je déclare v dans la boucle, et j'ai une nouvelle instance
chaque fois.

Si je veux réelement réduire la taille utilisée à la taille
minimum :

std::vector<T>( v ).swap( v ) ;

fait l'affaire. (Je ne prétends pas que ce soit une façon
intuitive de le faire. C'est néaumoins l'idiome consacré, bien
connu entretemps à tous les utilisateurs de la STL. Et la STL
n'a jamais cherché à être simple à utiliser.)

loufoque

unread,
Sep 19, 2006, 8:03:14 AM9/19/06
to
Sylvain a écrit :

> j'aime cette philosophie, la coexistence de variables statiques ET
> dynamiques me va très bien également; non, je ne vois pas comment
> efficacement sans passer.

Ben justement, il ne devrait pas y avoir coexistance.
Si tu veux du code de qualité, c'est-à-dire du code exception-safe,
toute donnée allouée dynamiquement est stockée dans un conteneur qui
gère sa durée de vie et qui répond à des sémantiques de valeur.
Donc le dynamique c'est uniquement dans des couches plus basses.


> je suis également d'accord avec les exigences du contrat de vector<>, je
> soulignais le trait pour indiquer qu'il a *IMHO* un caractère négatif.

Il n'y a pas de caractère négatif, c'est juste qu'on peut pas faire
autrement pour stocker des valeurs.


> il reste toutefois des cas où on a bien affaire à des instances qui
> doivent être non copiables (wrapper de resources physiques par exemple,
> ou gestionnaire d'accès à une source non multi-entrante, etc); de tels
> gestionnaires ne peuvent pas être regroupés / utilisés via un vector<>.

Normal, c'est physiquement impossible.
Tout ce que tu peux c'est centraliser les références à ces ressources et
la gestion de leur durée de vie.


> une alternative simple serait de pouvoir transmettre un T* (et même tout
> pointeur sur une classe assimilable à un T (T et ses sous classes)).

Ce qui ne change rien vu qu'on stocke des T, pas des T*.
Il faudra quand même une recopie.


> l'organisation interne de vector<> passe nécessairement par des
> pointeurs de la classe template

Non.
Comme il a déjà été dit, on stocke des T, pas des T*.
Je sais pas d'où te vient l'idée qu'on stocke des pointeurs des données
et pas les données elles-mêmes.

Tu aurais pourtant du, dans ton apprentissage du C++, réecrire un truc
semblable à std::vector.


> si la question était ouverte ("si" parce que heureusement 99% des
> utilisateurs se foutent de l'avis qui suit), le mérite de vector<> est
> son mécanisme allocator customisable

Oui enfin, ça sert uniquement à utiliser des mécanismes comme le
pooling, ce qui est plus le travail du système d'exploitation et de
l'environnement.


> (justement pour traiter les
> références)

Mais quel rapport ?


> et son redimensionnement, dans un second temps ses itérateurs.

Bon à la limite le redimensionnement d'accord (quoique y'a rien à
customiser, si on peut agrandir le segment on le fait et sinon on
recopie dans un endroit plus grand), mais les itérateurs !??
C'est du contigu donc de l'arithmétique de pointeurs de base.


> les 2 premiers points garantissent de travailler avec des références
> (T&), c'est comme cela que je travaille dès que possible donc il ne
> s'agirait pas de "travailler avec des pointeurs", surtout pas;

Le principe de base quand même des conteneurs, c'est qu'ils contiennent
des trucs.
On pourrait effectivement faire fonctionner std::vector avec des
références (en spécialisant en utilisant des pointeurs pour
l'implémentation) mais cela n'aurait aucun intérêt.
Une référence, ça ne fait que référencer, ce n'est pas la chose en question.


> par
> contre je ne trouverais pas gênant de faire un push(new T(...)).

Ce que tu peux très bien faire avec un conteneur de T*.
Ce qui néanmoins pose des problèmes d'exception safety pour la
libération des éléments contenus.

Ce que tu veux, en fait, c'est des conteneurs avec des sémantiques de
pointeurs accessoirement polymorphiques.
Pour ça, y'a les conteneurs de pointeurs de boost.

kanze

unread,
Sep 20, 2006, 3:54:07 AM9/20/06
to
loufoque wrote:
> Sylvain a écrit :

> > j'aime cette philosophie, la coexistence de variables
> > statiques ET dynamiques me va très bien également; non, je
> > ne vois pas comment efficacement sans passer.

> Ben justement, il ne devrait pas y avoir coexistance.

Bien sûr que si. C'est une des forces de C++, de supporter
plusieurs paradigmes. En l'occurance, selon la rôle du type dans
l'application, il risque d'avoir soit une sémantique de valeur,
soit une sémantique d'entité ; dans le premier cas, des
allocations dynamiques seront extrèmement rare (de même que je
ne vois gère d'intérêt dans « new int »), dans le deuxième, il
est probable que toutes les instances soient allouées
dynamiquement.

À un niveau plus bas, aussi, dans l'implémentation d'une valeur,
on peut choisir parfois d'allouer dynamiquement, parfois non, et
ça, dans certains cas, même à l'intérieur d'un seul type.
(Beaucoup des implémentations de std::string, par exemple, ont
un buffer local, et n'alloue dynamiquement que si la capacité
requise dépasse la taille du buffer -- c'est connu sous la
vocable de « small string optimization », en anglais.)

> Si tu veux du code de qualité, c'est-à-dire du code
> exception-safe, toute donnée allouée dynamiquement est stockée
> dans un conteneur qui gère sa durée de vie et qui répond à des
> sémantiques de valeur.

Pas du tout.

En général, il y a bien des utilisations de la mémoire dynamique
dans l'implémentation des types à sémantique de valeur, mais
cette utilisation est en général transparente aux clients du
type. Au niveau de l'application (c-à-d que tu n'es pas en train
d'implémenter de tels objets), pratiquement toutes les
allocations dynamiques concernent les objets d'entité. Et les
objets d'entité n'ont pas du tout une sémantique de valeur, tout
au contraire. On les crée suite à un évenemment (requête
utilisateur, etc.), et on les detruire suite à un autre
évenement. Au moins qu'ils n'existent toute la durée d'exécution
de l'application.

Et pour les exceptions, au moins qu'ils ont une sémantique
particulière associée à la fin de leur vie (ce qui est quand
même assez exceptionnel), le glaneur de cellules s'en occupe
très bien. Et c'est plus facile d'installer et de configurer le
collecteur de Boehm que d'installer et de configurer Boost, pour
avoir shared_ptr. (Mais ça vaut la peine d'installer Boost et de
s'en servir pour beaucoup d'autres raisons.)

> Donc le dynamique c'est uniquement dans des couches plus
> basses.

Au contraire. Si c'est vrai que les couches les plus basses
(p.e. l'implémentation de std::basic_string) puisse se servir de
la mémoire dynamique, pour quelqu'un qui n'écrit pas de telles
bibliothèques, le dynamique sert surtout pour les objets
d'entité, c-à-d typiquement dans la couche la plus haute.

> > je suis également d'accord avec les exigences du contrat de
> > vector<>, je soulignais le trait pour indiquer qu'il a
> > *IMHO* un caractère négatif.

> Il n'y a pas de caractère négatif, c'est juste qu'on peut pas
> faire autrement pour stocker des valeurs.

C-à-d qu'il se comporte comme un T[], à ce niveau-là.

[...]


> > l'organisation interne de vector<> passe nécessairement par des
> > pointeurs de la classe template

> Non.
> Comme il a déjà été dit, on stocke des T, pas des T*. Je sais
> pas d'où te vient l'idée qu'on stocke des pointeurs des
> données et pas les données elles-mêmes.

> Tu aurais pourtant du, dans ton apprentissage du C++, réecrire
> un truc semblable à std::vector.

Qu'il y a des lacunes dans la connaissance de la STL chez
Sylvain, c'est fort possible. Mais j'imagine quand même mal
quelqu'un qui a réécri un truc semblable de std::vector dans
leur apprentissage. Écrire un truc pareil exige l'utilisation
d'un certain nombre d'astuces que le programmeur moyen n'a pas
vraiment besoin de connaître (comme la séparation de
l'allocation et de l'initialisation), et qui font partie
vraiement du C++ avancé.

> > si la question était ouverte ("si" parce que heureusement
> > 99% des utilisateurs se foutent de l'avis qui suit), le
> > mérite de vector<> est son mécanisme allocator customisable

> Oui enfin, ça sert uniquement à utiliser des mécanismes comme
> le pooling, ce qui est plus le travail du système
> d'exploitation et de l'environnement.

Pas forcément. Je m'en suis déjà servi dans un std::map, pour le
mettre dans de la mémoire partagée entre plusieurs processus.
(Mais comme j'ai dit, historiquement, c'était pour pouvoir créer
un std::vector de plus de 64 Ko tout en compilant en modèle
small.)

loufoque

unread,
Sep 20, 2006, 10:17:17 AM9/20/06
to
kanze a écrit :

> Et pour les exceptions, au moins qu'ils ont une sémantique
> particulière associée à la fin de leur vie (ce qui est quand
> même assez exceptionnel), le glaneur de cellules s'en occupe
> très bien. Et c'est plus facile d'installer et de configurer le
> collecteur de Boehm que d'installer et de configurer Boost, pour
> avoir shared_ptr. (Mais ça vaut la peine d'installer Boost et de
> s'en servir pour beaucoup d'autres raisons.)

Tu supposes l'utilisation d'un ramasse-miettes (tiens, je connaissais
pas le terme "glaneur de cellules") ce qui te permet effectivement de ne
pas avoir à "wrapper" tes pointeurs vers des objets alloués
dynamiquement dans des objets à gestion mémoire automatique.
Néanmoins si on n'utilise pas ce genre de choses cela devrait être fait,
que ce soit dans des shared_ptr ou dans des trucs plus appropriés.

Je ne suis pas non plus un grand fan des shared_ptr.
En fait dans les développements que j'ai fait je n'ai jamais eu besoin
de ce genre de choses, mais cela vient sûrement du fait que je n'ai
jamais été confronté à des contraintes business, que je code plutôt des
trucs expérimentaux pour mon plaisir et que j'utilise très peu le
polymorphisme (pour du pimpl essentiellement) préférant la généricité.


> Qu'il y a des lacunes dans la connaissance de la STL chez
> Sylvain, c'est fort possible. Mais j'imagine quand même mal
> quelqu'un qui a réécri un truc semblable de std::vector dans
> leur apprentissage. Écrire un truc pareil exige l'utilisation
> d'un certain nombre d'astuces que le programmeur moyen n'a pas
> vraiment besoin de connaître (comme la séparation de
> l'allocation et de l'initialisation), et qui font partie
> vraiement du C++ avancé.

Truc semblable c'est à dire fort simplifié.
Il me paraît tout de même important de savoir réecrire les structures de
données pour savoir exactement comment elles fonctionnent et pouvoir en
déduire le coût de chacune des opérations.


> Pas forcément. Je m'en suis déjà servi dans un std::map, pour le
> mettre dans de la mémoire partagée entre plusieurs processus.
> (Mais comme j'ai dit, historiquement, c'était pour pouvoir créer
> un std::vector de plus de 64 Ko tout en compilant en modèle
> small.)

Oui en effet il y a aussi la possibilité de la mémoire partagée.
Mais bon dans ce cas-là ça ne s'utilise pas "normalement", il faut
passer par un framework genre boost.interprocess (anciennement boost.shmem)

J'ai jamais eu l'occasion d'utiliser ce genre de trucs cependant. Quand
je dois faire communiquer des processus j'utilise plutôt des pipes ou
des sockets.

Sylvain

unread,
Sep 20, 2006, 12:17:09 PM9/20/06
to
loufoque wrote on 20/09/2006 16:17:
>
>> Et pour les exceptions, au moins qu'ils ont une sémantique
>> particulière associée à la fin de leur vie (ce qui est quand
>> même assez exceptionnel), le glaneur de cellules s'en occupe
>> très bien. [...]
>
> Tu supposes l'utilisation d'un ramasse-miettes [...]

> Néanmoins si on n'utilise pas ce genre de choses cela devrait être fait,
> que ce soit dans des shared_ptr ou dans des trucs plus appropriés.

un "truc" comme un beau pointeur global ?

> [...] je code plutôt des

> trucs expérimentaux pour mon plaisir et que j'utilise très peu le
> polymorphisme (pour du pimpl essentiellement) préférant la généricité.

?!? tu peux m'expliquer "traitement polymorphe" vs "traitement
générique", stp ?
peut-être penses-tu à ce que l'on nomme parfois un "polymorphisme
statique" (ie des classes templates).

>>> Tu aurais pourtant du, dans ton apprentissage du C++, réecrire
>>> un truc semblable à std::vector.

hein ??!!??

il me semble que Stepanov a présenté ce projet au comité ANSI/ISO C++
entre fin 1993 et début 1994; à cette date, vois-tu, je n'étais plus à
l'école!

>> Qu'il y a des lacunes dans la connaissance de la STL chez
>> Sylvain, c'est fort possible.

ça je confirme, sans hésitation.

>> Mais j'imagine quand même mal
>> quelqu'un qui a réécri un truc semblable de std::vector dans
>> leur apprentissage. Écrire un truc pareil exige l'utilisation
>> d'un certain nombre d'astuces que le programmeur moyen n'a pas

>> vraiment besoin de connaître [...], et qui font partie


>> vraiement du C++ avancé.
>
> Truc semblable c'est à dire fort simplifié.
> Il me paraît tout de même important de savoir réecrire les structures de
> données pour savoir exactement comment elles fonctionnent et pouvoir en
> déduire le coût de chacune des opérations.

je sais écrire des "trucs" qui s'adaptent à ma ""philosophie"" (ici un
vecteur de T* qui retourne des T&), pour autant comprendre l'intérêt des
algo de la STL et en évaluer le coût, ne requiert pas IMHO de réécrire
la même chose (si on obtient bien la "même chose" autant utiliser
l'original, si - plus vraisemblable - on empile les erreurs de
compréhension et de codage, on mesurera un truc sans rapport).

>> [] mettre dans de la mémoire partagée entre plusieurs processus.


> J'ai jamais eu l'occasion d'utiliser ce genre de trucs cependant. Quand
> je dois faire communiquer des processus j'utilise plutôt des pipes ou
> des sockets.

James parlait de *partage* de données (une donnée dispo pour plusieurs
process distincts) pas de *communication* d'une donnée entre process.

sous Windows qui propose (impose) des APIs spécifiques pour de tels
données partagées, les allocators sont très utiles, sur PDA pour gérer
des objets persistants sur différentes cartes mémoires également (ce ne
sont que des exemples).

Sylvain.

Fabien LE LEZ

unread,
Sep 20, 2006, 2:53:35 PM9/20/06
to
On Wed, 20 Sep 2006 16:17:17 +0200, loufoque
<louf...@remove.gmail.com>:

>(tiens, je connaissais
>pas le terme "glaneur de cellules")

C'est un sale hack pour avoir les mêmes initiales que garbage
collector.
Z'auraient dû appeler ça "Garibaldi contrarié", ça aurait été plus
sympa...

loufoque

unread,
Sep 20, 2006, 6:48:02 PM9/20/06
to
Sylvain a écrit :

> un "truc" comme un beau pointeur global ?

Si l'objet doit exister jusqu'à la fermeture du programme, ça convient.
Mais bon c'est pas vraiment de ça que je parlais.


> ?!? tu peux m'expliquer "traitement polymorphe" vs "traitement
> générique", stp ?
> peut-être penses-tu à ce que l'on nomme parfois un "polymorphisme
> statique" (ie des classes templates).

Ça dépend du sens qu'on donne à polymorphisme, je parlais du
polymorphisme dynamique d'inclusion, qui introduit un coût à l'exécution
via l'utilisation de fonctions membres virtuelles et qui s'utilise via
des pointeurs ou références sur des bases.
Je préfère l'usage des templates avec notamment le policy-based design.


> hein ??!!??
>
> il me semble que Stepanov a présenté ce projet au comité ANSI/ISO C++
> entre fin 1993 et début 1994; à cette date, vois-tu, je n'étais plus à
> l'école!

Quel rapport ?
Si tu veux développer en C++, il faut bien apprendre à un moment ou à un
autre. Que ce soit à l'école, en autodidacte, ou dans un stage.


> je sais écrire des "trucs" qui s'adaptent à ma ""philosophie"" (ici un
> vecteur de T* qui retourne des T&), pour autant comprendre l'intérêt des
> algo de la STL et en évaluer le coût, ne requiert pas IMHO de réécrire
> la même chose (si on obtient bien la "même chose" autant utiliser
> l'original, si - plus vraisemblable - on empile les erreurs de
> compréhension et de codage, on mesurera un truc sans rapport).

Un des avantages de C++ est que les structures de données ne sont pas
dans le langage. Savoir donc comment en créer une avec celui-ci me
paraît important.
D'autant plus que savoir écrire les structures de données usuelles de
manière générique est une connaissance que chaque développeur devrait
avoir. Il me semble d'ailleurs que ce genre de chose est enseigné en
début de tout cursus d'études en informatique en algorithmie.

Il faut effectivement mieux utiliser l'original qui est sûrement testé
et optimisé.
À part bien sûr dans les cas où tu as besoin de choses plus personnalisées.


> James parlait de *partage* de données (une donnée dispo pour plusieurs
> process distincts) pas de *communication* d'une donnée entre process.

Je sais ce qu'est la mémoire partagée.
Pour travailler avec plusieurs processus cependant je n'en ai pas eu
besoin, préférant la communication.

>
> sous Windows qui propose (impose) des APIs spécifiques pour de tels
> données partagées, les allocators sont très utiles, sur PDA pour gérer
> des objets persistants sur différentes cartes mémoires également (ce ne
> sont que des exemples).

Je vois pas trop l'intérêt d'avoir des choses persistantes en dehors
d'un programme (à part sous l'aspect d'une base de données).
Autant utiliser un serveur d'applications, non ?
Enfin c'est vrai que dans l'embarqué, la machine elle-même est plus ou
moins un serveur d'applications.

kanze

unread,
Sep 21, 2006, 2:56:28 AM9/21/06
to
loufoque wrote:
> kanze a écrit :

> > Et pour les exceptions, au moins qu'ils ont une sémantique
> > particulière associée à la fin de leur vie (ce qui est quand
> > même assez exceptionnel), le glaneur de cellules s'en occupe
> > très bien. Et c'est plus facile d'installer et de configurer le
> > collecteur de Boehm que d'installer et de configurer Boost, pour
> > avoir shared_ptr. (Mais ça vaut la peine d'installer Boost et de
> > s'en servir pour beaucoup d'autres raisons.)

> Tu supposes l'utilisation d'un ramasse-miettes (tiens, je connaissais
> pas le terme "glaneur de cellules") ce qui te permet effectivement de ne
> pas avoir à "wrapper" tes pointeurs vers des objets alloués
> dynamiquement dans des objets à gestion mémoire automatique.

C'est vrai que je conçois mal commencer un projet C++
aujourd'hui sans Boost ni le collecteur de Boehm. Ni l'un ni
l'autre ne fait partie de la norme (encore), mais les deux sont
assez portables, et se rendent de services importantes.

> Néanmoins si on n'utilise pas ce genre de choses cela devrait être fait,
> que ce soit dans des shared_ptr ou dans des trucs plus appropriés.

Non plus. Une des raisons pourquoi un glaneur de cellules est si
important, c'est précisement parce que les alternatives du genre
shared_ptr ne suffisent pas. Qu'on les utilise pour certaines
choses, certes, mais c'est loin d'être une solution univerelle.

> Je ne suis pas non plus un grand fan des shared_ptr.
> En fait dans les développements que j'ai fait je n'ai jamais eu besoin
> de ce genre de choses, mais cela vient sûrement du fait que je n'ai
> jamais été confronté à des contraintes business, que je code plutôt des
> trucs expérimentaux pour mon plaisir et que j'utilise très peu le
> polymorphisme (pour du pimpl essentiellement) préférant la généricité.

Je conçois bien que beaucoup dépend de l'application. Dans mon
cas, j'ai de l'expérence importante dans deux domaines
distincts : la banque (côté gestion des comptes, etc. -- très
peu sur les logiciels de marché), et la téléphonie (côté
opérationnelle). A priori, je crois que ces expériences
s'exterpôlent assez bien vers une bonne partie de la gestion ou
du contrôle en temps-réel ; c'est certain, en revanche, qu'il y
a des domaines tout à fait différents, genre jeux ou les
applications numériques de recherche.

Ce qui est commun à toutes mes applications, c'est qu'on modèle
une partie du domaine d'application au moyen des objets
d'entité ; ces objets apparaissent et s'en vont en fonction des
évenemments extérieurs. Et que la plupart des autres types sont
des types de valeur, qui ne sont jamais alloués dynamiquement.

> > Qu'il y a des lacunes dans la connaissance de la STL chez
> > Sylvain, c'est fort possible. Mais j'imagine quand même mal
> > quelqu'un qui a réécri un truc semblable de std::vector dans
> > leur apprentissage. Écrire un truc pareil exige l'utilisation
> > d'un certain nombre d'astuces que le programmeur moyen n'a pas
> > vraiment besoin de connaître (comme la séparation de
> > l'allocation et de l'initialisation), et qui font partie
> > vraiement du C++ avancé.

> Truc semblable c'est à dire fort simplifié.
> Il me paraît tout de même important de savoir réecrire les structures de
> données pour savoir exactement comment elles fonctionnent et pouvoir en
> déduire le coût de chacune des opérations.

Mais jusqu'où ? Une partie fondamentale des collections STL,
c'est la séparation de l'allocation et de l'initialisation. Une
possibilité du C++ qui m'a servi aussi dans mes collections
pré-norme. Mais une possibilité qui ne m'a jamais servi que pour
l'implémentation de de telles classes, qui ne fait pas
forcément partie du bagage du programmeur C++ de base, et qui ne
doit certainement pas faire partie des cours de base de C++.

> > Pas forcément. Je m'en suis déjà servi dans un std::map, pour le
> > mettre dans de la mémoire partagée entre plusieurs processus.
> > (Mais comme j'ai dit, historiquement, c'était pour pouvoir créer
> > un std::vector de plus de 64 Ko tout en compilant en modèle
> > small.)

> Oui en effet il y a aussi la possibilité de la mémoire partagée.
> Mais bon dans ce cas-là ça ne s'utilise pas "normalement", il faut
> passer par un framework genre boost.interprocess (anciennement boost.shmem)

Pourquoi ? Il faut bien synchroniser les accès ; dans notre
cas, on s'est servi des pthread_rwlock directement. Sinon, on
a remplacé l'allocateur, et ça a suffi. (La mémoire partagée, en
l'occurance, venait de mmap. Je ne sais pas pourquoi, mais c'est
beaucoup plus simple et plus efficace que le shmem de Posix.)

> J'ai jamais eu l'occasion d'utiliser ce genre de trucs cependant. Quand
> je dois faire communiquer des processus j'utilise plutôt des pipes ou
> des sockets.

En général, moi aussi. C'était un cas exceptionnel.

kanze

unread,
Sep 21, 2006, 3:09:20 AM9/21/06
to
Sylvain wrote:
> loufoque wrote on 20/09/2006 16:17:

> >> Et pour les exceptions, au moins qu'ils ont une sémantique
> >> particulière associée à la fin de leur vie (ce qui est quand
> >> même assez exceptionnel), le glaneur de cellules s'en occupe
> >> très bien. [...]

> > Tu supposes l'utilisation d'un ramasse-miettes [...]
> > Néanmoins si on n'utilise pas ce genre de choses cela devrait être fait,
> > que ce soit dans des shared_ptr ou dans des trucs plus appropriés.

> un "truc" comme un beau pointeur global ?

Tout dépend, mais je me sers encore beaucoup des pointeurs
bruts. (C'est ce que je crois que tu veux dire. Des variables
globales, pointeur ou d'autres, il y en a fort peu dans mes
programmes.)

La plupart du temps, quand je me sers d'un pointeur intelligent,
ce n'est pas que pour la gestion de la durée de vie ; c'est
vraiment une question d'« ownership ». Donc, par exemple, dans
mes interfaces entre des threads, je me sers beaucoup
d'auto_ptr -- une fois que la propriété de l'objet a été passé
à l'autre thread, l'accès dans le premier thread devient
impossible.

(En passant : pourquoi est-ce que parler de l'« ownership »
d'un objet me semble tout à fait naturel en anglais, mais que
l'utilisation du mot « propriété » dans le même contexte en
français ne me semble pas juste ?)

> > [...] je code plutôt des
> > trucs expérimentaux pour mon plaisir et que j'utilise très peu le
> > polymorphisme (pour du pimpl essentiellement) préférant la généricité.

> ?!? tu peux m'expliquer "traitement polymorphe" vs "traitement
> générique", stp ?

C'est qu'il utilise le vocabulaire détourné de la STL. Où les
itérateurs ne sont pas de vrais itérateurs, et la généricité est
limitée à la compilation:-).

> peut-être penses-tu à ce que l'on nomme parfois un "polymorphisme
> statique" (ie des classes templates).

De même façon que dans le langage courant de l'informatique en
dehors de la communité C++, la généricité s'implémente souvent
au moyen de l'héritage.

> >>> Tu aurais pourtant du, dans ton apprentissage du C++, réecrire
> >>> un truc semblable à std::vector.

> hein ??!!??

> il me semble que Stepanov a présenté ce projet au comité ANSI/ISO C++
> entre fin 1993 et début 1994; à cette date, vois-tu, je n'étais plus à
> l'école!

Ce qui veut dire alors que tu as dû implémenter quelque chose de
semblable, parce qu'avant, on n'avait que ce qu'on avait
implémenté nous-même. (En fait, il y avait de bonnes
bibliothèques disponibles : USL, Booch, etc. Mais pour diverses
raisons politiques, on ne pouvait pas toujours s'en servir.)

kanze

unread,
Sep 21, 2006, 3:31:29 AM9/21/06
to
loufoque wrote:
> Sylvain a écrit :

> > un "truc" comme un beau pointeur global ?

> Si l'objet doit exister jusqu'à la fermeture du programme, ça
> convient. Mais bon c'est pas vraiment de ça que je parlais.

Mais de quels objets est-ce que tu parlais ? Je n'utilise pas
l'allocation dynamique pour des objets de type valeur, qui sont
soit sur la pile, soit des membres d'autres classes. Je
l'utilise surtout pour des objets de type entité, dont la durée
de vie est arbitraire.

(Il y a une exception, surtout avec des objets « agents »,
pour lesquels on aimerait d'une certain côté une sémantique de
valeur, mais qui doivent être polymorphiques. Dans ces cas-là,
si pour une raison quelconque, je n'ai pas de glaneur de
cellules, je me sers de boost::shared_ptr. Mais c'est loins
d'être un cas universel.)

> > ?!? tu peux m'expliquer "traitement polymorphe" vs "traitement
> > générique", stp ?
> > peut-être penses-tu à ce que l'on nomme parfois un "polymorphisme
> > statique" (ie des classes templates).

> Ça dépend du sens qu'on donne à polymorphisme, je parlais du
> polymorphisme dynamique d'inclusion, qui introduit un coût à l'exécution
> via l'utilisation de fonctions membres virtuelles et qui s'utilise via
> des pointeurs ou références sur des bases.

Tu as mesuré, ce coût ? Au moins d'avoir des fonctions qui font
vraiment rien, je doute que la différence soit signifiante.
(Dans certains cas, évidemment, si, et je ne concevois pas de
classe équivalente de vector où l'operator[] soit virtuel.
Encore que... dans mes ArrayOf pré-standard, j'utilisais des
techniques de Barton et Nackmann pour avoir la virtuellité par
défaut, mais de pouvoir le circomventer quand je savais le type
et que les performances en souffraient.)

> Je préfère l'usage des templates avec notamment le
> policy-based design.

Tout dépend de ce qu'on fait : les templates permettent la
validation des prédicates sur le type, lors de la compilation,
par exemple, tandis qu'avec l'héritage et des fonctions
virtuelles, il faudrait compter plutôt sur une erreur lors de
l'exécution. En revanche, s'il s'agit réelement d'offrir
plusieurs façons de faire quelque chose -- ce qu'on entend par
le polymorphisme en général, le polymorphisme dynamique est
nettement plus souple. En fait, les deux possibilités servent à
deux choses différentes, et les cas où on peut se servir de
l'une ou de l'autre indifféremment sont assez rares.

Je te conseille fortement le livre de Barton et Nackman. C'est
daté, aujourd'hui, mais c'est encore l'un des rares livres qui
considèrent réelement les deux possibilités, en évaluant l'une
et l'autre. (D'après ce que j'ai entendu dire, c'est aussi le
cas du livre dont David Abraham est le co-auteur. Je n'ai pas
encore eu le temps de le lire, mais si je peux juger d'après ses
contributions dans les groupes de news anglophone, ça vaudrait
la peine.)

> > hein ??!!??

> > il me semble que Stepanov a présenté ce projet au comité
> > ANSI/ISO C++ entre fin 1993 et début 1994; à cette date,
> > vois-tu, je n'étais plus à l'école!

> Quel rapport ?
> Si tu veux développer en C++, il faut bien apprendre à un
> moment ou à un autre. Que ce soit à l'école, en autodidacte,
> ou dans un stage.

Apprendre quoi ? Pourvoir écrire une bonne classe de tableau ou
de chaîne, c'est une des choses la plus difficile qui soit en
C++. Il exige en plus certaines connaissances qui ne sont pas
vraiment d'une utilité générale, et qui ne servent que dans de
telles classes. Qu'on les enseigne dans une classe du C++
avancé, je veux bien, mais je ne vois aucun intérêt à ce que la
plupart des programmeurs C++ les étudient.

kanze

unread,
Sep 21, 2006, 3:44:06 AM9/21/06
to
loufoque wrote:

[Je m'excuse, mais je ne peux pas résister:-)...]

> Je vois pas trop l'intérêt d'avoir des choses persistantes en dehors

> d'un programme.

Ton programme fait quelque chose, non ? Tu en jettes les
résultats ? Ou est-ce qu'ils persistent quelque part (au moins
dans ta têtes, parce que tu les as vu affichés sur l'écrans) ?

(Ceci dit, je ne comprends pas la polémique. Tu as simplement
dit que tu n'avais pas eu besoin de la mémoire partagée ; que
la communication par sockets a suffit pour tes applications. Je
ne vois pas de problème avec ça, dans la mesure où tu acceptes
que ce n'est pas une vérité universelle. Les sockets, la mémoire
partagée, et toute la reste ne sont que des outils, à utiliser à
bonne échéance, et non simplement parce qu'ils sont là. Les
sockets ont l'avantage -- énorme -- qu'ils fonctionnent même
quand les processus sont sur deux machines différentes ; la
mémoire partagée ne marche pas très bien quand le serveur est
sur un Sparc et le client sur une machine Windows. La mémoire
partagée a l'avantage qu'elle n'exige pas la sérialisation ; si
la quantité de données en question est importante, ça peut avoir
un effet non-négligeable sur les performances. Ce sont en fait
deux outils différents, pour résoudre des problèmes différents.
Et qu'à mon avis, quand il y a un choix, c'est plus souvent
entre un processus multi-thread, ou plusieurs processus avec de
la mémoire partagée, qu'entre sockets et mémoire partagée.)

Alain Gaillard

unread,
Sep 21, 2006, 4:36:20 AM9/21/06
to
kanze a écrit :


> (En passant : pourquoi est-ce que parler de l'« ownership »
> d'un objet me semble tout à fait naturel en anglais, mais que
> l'utilisation du mot « propriété » dans le même contexte en
> français ne me semble pas juste ?)

Je partage ton avis. Je préfère : détention de l'objet.

> De même façon que dans le langage courant de l'informatique en
> dehors de la communité C++, la généricité s'implémente souvent
> au moyen de l'héritage.

Pas nécessairement. Pas dans les langages fonctionnels comme Haskell ou
Clean par exemple.


--
Alain

loufoque

unread,
Sep 21, 2006, 7:01:09 AM9/21/06
to
kanze a écrit :

> Ton programme fait quelque chose, non ? Tu en jettes les
> résultats ? Ou est-ce qu'ils persistent quelque part (au moins
> dans ta têtes, parce que tu les as vu affichés sur l'écrans) ?

Je veux bien les sauvegarder sur le disque dur, mais les laisser en RAM
(avec la mémoire partagée donc) je vois pas trop l'intérêt.

loufoque

unread,
Sep 21, 2006, 7:05:44 AM9/21/06
to
kanze a écrit :

> (En passant : pourquoi est-ce que parler de l'« ownership »
> d'un objet me semble tout à fait naturel en anglais, mais que
> l'utilisation du mot « propriété » dans le même contexte en
> français ne me semble pas juste ?)

Peut-être à cause de l'ambiguïté avec « propriety »

Message has been deleted

loufoque

unread,
Sep 21, 2006, 7:26:01 AM9/21/06
to
kanze a écrit :

> Pourquoi ? Il faut bien synchroniser les accès ; dans notre
> cas, on s'est servi des pthread_rwlock directement. Sinon, on
> a remplacé l'allocateur, et ça a suffi. (La mémoire partagée, en
> l'occurance, venait de mmap. Je ne sais pas pourquoi, mais c'est
> beaucoup plus simple et plus efficace que le shmem de Posix.)

Ce que je voulais dire c'est que du coup on ne créé pas le conteneur
normalement et on y accède avec un pointeur.
Donc ce n'est pas un simple remplacement d'allocateur par rapport à une
utilisation normale, on passe par une espèce de "fabrique" qui maintient
des identifiants et des offsets pour chaque variable.

kanze

unread,
Sep 21, 2006, 11:51:00 AM9/21/06
to

Mais quel rapport entre la mémoire partagée et la persistance ?
Quand mes programmes terminent, la mémoire partagée cesse
d'exister.

kanze

unread,
Sep 21, 2006, 11:58:12 AM9/21/06
to
loufoque wrote:
> kanze a écrit :

> > Pourquoi ? Il faut bien synchroniser les accès ; dans notre
> > cas, on s'est servi des pthread_rwlock directement. Sinon,
> > on a remplacé l'allocateur, et ça a suffi. (La mémoire
> > partagée, en l'occurance, venait de mmap. Je ne sais pas
> > pourquoi, mais c'est beaucoup plus simple et plus efficace
> > que le shmem de Posix.)

> Ce que je voulais dire c'est que du coup on ne créé pas le
> conteneur normalement et on y accède avec un pointeur.

D'accord. C'est vrai que pour les clients, c'est un peu
différent -- on fait un coup de mmap, et du coup, on a une
collection déjà faite, et même bien remplie.

> Donc ce n'est pas un simple remplacement d'allocateur par
> rapport à une utilisation normale, on passe par une espèce de
> "fabrique" qui maintient des identifiants et des offsets pour
> chaque variable.

Pas vraiment. Il y a bien un calcul de l'adresse après le mmap,
mais c'est tout. Ensuite, il fonctionne exactement comme
n'importe quel std::map. C'est vrai même pour le serveur, que
j'y pense. Sauf qu'il calcule l'adresse exactement comme font
les clients, puis il se sert d'un new de placement. Et ensuite,
c'est un std::map tout à fait comme un autre.

Sylvain

unread,
Sep 21, 2006, 12:50:09 PM9/21/06
to
kanze wrote on 21/09/2006 09:09:
>
> (En passant : pourquoi est-ce que parler de l'« ownership »
> d'un objet me semble tout à fait naturel en anglais, mais que
> l'utilisation du mot « propriété » dans le même contexte en
> français ne me semble pas juste ?)

ou encore "possède" vs "utilise".

> Ce qui veut dire alors que tu as dû implémenter quelque chose de
> semblable, parce qu'avant, on n'avait que ce qu'on avait
> implémenté nous-même.

tout à fait, pour les concepts de string et array; un temps marqué par
les OS sur lesquels j'utilisais ces outils (par exemple la toolbox MacOS
utilisait bcp de string-pascal, donc mes chaines était pascal et zéro
terminée); puis OS indépendant mais métier dépendant (la façon dont les
structures de données sont utilisées, par exemple souvent copiées ou
jamais, est marqué par la nature de l'application).

de fait switcher à la STL n'a jamais été ni requis ni parfois possible.

Sylvain.

loufoque

unread,
Sep 21, 2006, 12:51:36 PM9/21/06
to
kanze a écrit :

> Mais quel rapport entre la mémoire partagée et la persistance ?
> Quand mes programmes terminent, la mémoire partagée cesse
> d'exister.

Ben c'est Sylvain qui a commencé à parler de mémoire partagée pour faire
des objets persistants.
Je ne fais que dire que je voyais pas l'intérêt d'un tel type de
persistance. (après, je connais pas vraiment la programmation sur PDA
non plus)

Alain Gaillard

unread,
Sep 21, 2006, 1:04:31 PM9/21/06
to
Sylvain a écrit :


> utilisait bcp de string-pascal, donc mes chaines était pascal et zéro
> terminée);

Des chaīnes pascal zéro terminées ? Ah bon ?

--
Alain

Sylvain

unread,
Sep 21, 2006, 1:25:23 PM9/21/06
to
loufoque wrote on 21/09/2006 18:51:
> kanze a écrit :
>
>> Mais quel rapport entre la mémoire partagée et la persistance ?
>> Quand mes programmes terminent, la mémoire partagée cesse
>> d'exister.
>
> Ben c'est Sylvain qui a commencé à parler de mémoire partagée pour faire
> des objets persistants.

non j'ai dit que définir *son* mécanisme allocator permettait de gérer
des objets partagées en mémoire *ou* de gérer des objets persistants.

ce sont 2 exemples distincts, merci de relire.

Sylvain.

Sylvain

unread,
Sep 21, 2006, 1:28:18 PM9/21/06
to
Alain Gaillard wrote on 21/09/2006 19:04:
>
> Des chaînes pascal zéro terminées ? Ah bon ?

je disais /à la fois/ préfixée de leur longueur (avec contraite de
longueur max.) pour être transmisse à certaines API /et/ zéro terminée
pour être utilisée par les fonctions de string.h

c'était juste une anecdote, plus personne aujourd'hui n'utilise la
toolbox MacOS ...

Sylvain.

kanze

unread,
Sep 22, 2006, 2:45:23 AM9/22/06
to
Sylvain wrote:
> kanze wrote on 21/09/2006 09:09:

> > (En passant : pourquoi est-ce que parler de l'« ownership »
> > d'un objet me semble tout à fait naturel en anglais, mais que
> > l'utilisation du mot « propriété » dans le même contexte en
> > français ne me semble pas juste ?)

> ou encore "possède" vs "utilise".

Oui. Ce dont on a besoin, c'est en fait une famille de mots :
il y a le verbe, l'agent, le fait... Avec « possède », je ne
vois pas trop ce qui me servira d'agent : « possesseur » ne
me plaît pas outremesure.

Et avec la suggestion d'« utilise », tu as mis le doigt sur
une partie du problème. Historiquement, on parlait beaucoup
d'« ownership » par rapport à qui est responsable du delete ;
c'était un concepte très important pour éviter les fuites de
mémoire. Aujourd'hui, avec la disponabilité du collecteur de
Boehm, cet aspect a perdu beaucoup d'importance. En revanche,
avec les threads, des considérations de qui a droit d'accéder
quand se mettent en avant, et aujourd'hui, quand je parle
d'« ownership », c'est plus souvent dans ce sens-là (et
l'« owner » est souvent exprimé en termes de thread, et non
d'objet). Il y a donc en fait plusieurs conceptes en
considération.

Loïc Joly

unread,
Sep 22, 2006, 2:57:15 PM9/22/06
to
kanze a écrit :

>
> (En passant : pourquoi est-ce que parler de l'« ownership »
> d'un objet me semble tout à fait naturel en anglais, mais que
> l'utilisation du mot « propriété » dans le même contexte en
> français ne me semble pas juste ?)

Peut-être parce qu'en français, propriété peux aussi dire "property" ?

J'utilise généralement possèder/possession

--
Loïc

0 new messages