OVH Cloud OVH Cloud

std::vector

25 réponses
Avatar
JM
Bonjour

J'ai enfin décidé de me modernisé et d'utiliser la STl pour la gestion
des tableaux (ne pas s'occuper de l'allocation, c'est bien sympathique)

voilà, j'ai un truc du genre :

1) sans STL

void f(CPoint *points);

CPoint ptTab[NBMAX]

L'appel f(ptTab) marche sans problème.

Avec la STl, je crée :

vector<CPoint> ptTab;


Si je fais f(ptTab) ça marche pas
Le seul truc que j'ai réussi à faire marcher est f(&ptTab[0])

Est-ce normal où ai-je raté quelque chose?

Merci

10 réponses

1 2 3
Avatar
kanze
Sylvain wrote:
Franck Branjonneau wrote on 13/09/2006 01:17:

Si cela fonctionne, ce n'est que le fruit du hasard (des
choix de ton implémentation) : std::vector<>::begin()
retourne un iterateur.


c'est quoi un "iterateur"


Un type qui remplit certaines conditions. Typiquement, c'est une
classe, soit définit (ici) dans std::vector, soit définit
ailleurs, avec un typedef dans std::vector. Donc, par exemple,
avec ton code, VC++ 2005 me dit « cannot convert from
'std::_Vector_iterator<_Ty,_Alloc>' to 'CPoint*', et g++ me dit
« cannot convert
'__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<CPoint*,
__gnu_norm::vector<CPoint, std::allocator<CPoint> > >,
__gnu_debug_def::vector<CPoint, std::allocator<CPoint> > >' to
'CPoint*' ».

Dans d'anciennes implémentations, dans le cas précis de
std::vector (mais non pour les autres collections), c'était un
typedef à un pointeur. Ce qui est permis par la norme, mais ce
qui n'est que rarement le cas dans les implémentations modernes.

(ça se (s'auto) définit comment?


C'est défini pour tout collection dans la STL. Le concepte
d'itérateur, d'ailleurs, est vraiment à la base de la STL. Je ne
crois pas qu'on puisse parler de la STL sans le connaître.

quels sont ses propres opérateurs de cast?)

Rm: ça retourne un "iterateur" ou un "iterateur const".


Selon le cas, begin renvoie iterator ou const_iterator. (Ou plus
exactement std::container<type>::iterator ou
std::container<type::const_iterator -- dans le cas qui nous
intéresse, donc, std::vector< CPoint >::iterator ou
std::vector< CPoint »:const_iterator. Qui peut être un
typedef -- c'est le cas et avec VC++ et avec g++.)

--
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
kanze
JM wrote:

Normal, mais pourquoi f n'est-elle pas déclarée:

void f(std::vector<CPoint>& points);

qui est la forme à laquelle on s'attend si on utilise la SL.


Bé d'abord parce que je découvre la STL,


Au fond, ce que conseille Jean-Marc n'a rien à faire avec la
bibliothèque standard. En général, en C++, si tu veux qu'un
paramètre d'une fonction ait une sémantique de référence, tu
utilises une référence ; si tu veux une sémantique de valeur,
tu passes par valeur, quitte à optimiser avec une référence
const dans le cas où la copie est très chère (souvent le cas
avec std::vector) et que tu ne modifies pas le paramètre dans la
fonction.

Historiquement, tu te trouves dans la situation C, où les
paramètres se passent uniquement par valeur, MAIS où les
tableaux n'étaient pas des types comme les autres. Tandis qu'un
std::vector est un type comme n'importe quel autre type, sans
règles spéciales abhérantes et bizarres.

ensuite cela m'évite de tout changer dans un premier temps,
puis cela fait des déclarations à rallonge :o)


Si tu changes le type d'un paramètre, il faut bien changer la
déclaration des fonctions qui le prend, non ? Sinon, je dirais
qu'il y a un vrai problème.

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

Si tu changes le type d'un paramètre, il faut bien changer la
déclaration des fonctions qui le prend, non ? Sinon, je dirais
qu'il y a un vrai problème.



Jusqu'à présent, quand je définissais des tableaux, j'utilisais toujours
des trucs du genre :

A machin[NB_MAX]

f(A *pmachin);

Puis j'appelais de la manière suivante : f(machin);

En gros.

Pourquoi?
Parce que j'avais appris comme ça il y a trèèèèèès longtemps.

Pendant longtemps j'ai eu la flemme d'apprendre la STL, mon projet
initial ne devant pas prendre tant d'ampleur.
Donc je déclarais les tableaux à l'ancienne, mais maintenant, j'estime
que dans un programme moderne, si on limite de manière définitive la
taille des tableaux (par exemple des tableaux de divers objets
graphiques), pour l'utilisateur c'est trop contraignant.

D'où passage à la STL qui permet de s'affranchir de ça et de gérer plus
facilement la mémoire.

Donc, dans un premier temps, j'essaie de modifier le moins de chose (en
gros, uniquement les déclaration quand c'est possible) en essayant de
réutiliser l'existant.
Si je modifie tout d'un coup, c'est le plantage assuré

Avatar
JM

class Points : public vector<CPoint> {


Ce n'est pas en général une bonne idée d'hériter de std::vector.
Il n'a pas été conçu pour servir de classe de base.



Oui mais si ça marche, j'avoue que c'est le principal.
Respecter la norme pour la norme, ce n'est pas mon truc.
En un truc qui la respecte à la lettre, et un autre un peu moins mais
valable et plus lisible, mon choix est vite fait.


Avatar
Fabien LE LEZ
On Wed, 13 Sep 2006 13:49:50 +0200, JM :

class Points : public vector<CPoint> {


Ce n'est pas en général une bonne idée d'hériter de std::vector.
Il n'a pas été conçu pour servir de classe de base.


Oui mais si ça marche,


Ça marche parfois.



Avatar
Franck Branjonneau
Sylvain écrivait:

Franck Branjonneau wrote on 13/09/2006 01:14:

void f(std::vector<CPoint>& points);


Bé d'abord parce que je découvre la STL, ensuite cela m'évite de tout
changer dans un premier temps, puis cela fait des déclarations à
rallonge :o)


inline void f(std::vector<CPoint>& points) { return f(&points[0]); }


tu plaisantes ?


Non, pourquoi ?

--
Franck Branjonneau




Avatar
kanze
JM wrote:

class Points : public vector<CPoint> {


Ce n'est pas en général une bonne idée d'hériter de std::vector.
Il n'a pas été conçu pour servir de classe de base.


Oui mais si ça marche, j'avoue que c'est le principal.


Pour quelle définition de marcher ? C'est un piège à con pour
celui qui te suit.

Respecter la norme pour la norme, ce n'est pas mon truc.


La norme n'en dit rien ici. C'est plutôt du bon sens qui parle.

En un truc qui la respecte à la lettre, et un autre un peu
moins mais valable et plus lisible, mon choix est vite fait.


Le problème, c'est justement que ce genre d'héritage nuit
énormement à la lisibilité. (Les conversions implicites aussi,
évidemment. Peut-être même plus.)

--
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
kanze
JM wrote:

Si tu changes le type d'un paramètre, il faut bien changer la
déclaration des fonctions qui le prend, non ? Sinon, je dirais
qu'il y a un vrai problème.


Jusqu'à présent, quand je définissais des tableaux, j'utilisais tou jours
des trucs du genre :

A machin[NB_MAX]

f(A *pmachin);

Puis j'appelais de la manière suivante : f(machin);

En gros.

Pourquoi?
Parce que j'avais appris comme ça il y a trèèèèèès longtemp s.


Et tu as appris comme ça parce que c'est comme ça qu'on fait en
C, parce qu'en C, les tableaux, c'est bien des types deuxième
classe.

En C++, on a une meilleur alternative.

Pendant longtemps j'ai eu la flemme d'apprendre la STL, mon
projet initial ne devant pas prendre tant d'ampleur.
Donc je déclarais les tableaux à l'ancienne, mais maintenant,
j'estime que dans un programme moderne, si on limite de
manière définitive la taille des tableaux (par exemple des
tableaux de divers objets graphiques), pour l'utilisateur
c'est trop contraignant.

D'où passage à la STL qui permet de s'affranchir de ça et de
gérer plus facilement la mémoire.

Donc, dans un premier temps, j'essaie de modifier le moins de
chose (en gros, uniquement les déclaration quand c'est
possible) en essayant de réutiliser l'existant.

Si je modifie tout d'un coup, c'est le plantage assuré


Et si tu ne le fait pas, ça ne va pas compiler.

--
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
Sylvain
kanze wrote on 13/09/2006 10:40:

typedef peut aider si tu t'obliges à changer tes interfaces,
cela risque de ne pas tout régler si tu utilises des libs
externes attendant un CPoint*


Si c'est une classe à lui, il a toutes les chances à avoir accès
à toutes les fonctions concernées.


soit, mais cela peut être la classe (archi basic) des MFC ...

La façon naturelle en C++ de passer un paramètre par référence,
c'est d'utiliser une référence. Si on a un problème avec des
bibliothèques externe, on le résoud en général avec un wrapper
(modèle décorateur) qui fait les conversions nécessaires.


si l'objet décoré est le vector<> et si le decorator est celui
qui fournit un opérateur de cast vers CPoint*, le problème
restera identique: obtenir ce pointeur depuis le vector<>.

class Points : public vector<CPoint> {


Ce n'est pas en général une bonne idée d'hériter de std::vector.
Il n'a pas été conçu pour servir de classe de base.


cela est propre à vector ou est vrai pour la plupart des classes
templates de la STL ?

public:
Points(){}
operator CPoint* () { return begin(); }


Et ça, évidemment, ne se compile pas, sinon que par hazard avec
un ancien compilateur. (Il se compilait avec VC++ 6.0 ou g++
2.95.2, mais il ne se compile pas avec VC++ 2005 ni avec g++
4.1.0.)


le "hasard" est ici simplement la définition des libs fournies
avec les compilo cités, c'est assez déterministe comme hasard.

btw, l'environnement de dév. "Studio" est en version 2005,
les outils C++ (le "produit") sont en version 8, le compilo
(le "fichier" compiler) en version 14.

le problème connu comme "ne se compile pas" est lié à la
version de la STL utilisé, non à celle du compilateur;
dès lors indiquer que cela ne peut fonctionner avec telle
version de la STL, même si cela pouvait être valide avec
telle autre version serait plus instructif.

cela posant d'ailleurs le problème de l'identification de
la version de la STL utilisée, comment la connaitre ?
(j'ai noté un copyright en commentaire en fin de ses
fichiers, ce n'est pas vraiment la réponse attendue).

cela interroge également sur la compatibilité ascendante
des STL, n'étant pas adepte du hasard, comment celle-ci
est garantie ?

plus généralement, existe-t-il une définition normative
des API (donc hors implémentation) qu'une librairie dite
STL doit satisfaire ?

pour revenir sur le fond de ma proposition, que je
reformule en: "est-il possible de changer une déclaration
a-la-mano (new CPoints, etc) par une utilsation de vector<>
*sans* devoir modifier toutes ses procédures de traitement ?"

- je pense inintéressant de savoir si "begin()" se compile
ou si "return &at(0);" est mieux; on codera cet opérateur
une seule et unique fois, il peut changer, j'ai pu donner
un mauvais nom, on s'en tape

- car le point est bcp plus:
* vector<> jouant (déjà) un role de décorateur:
peux-t-on attendre d'une prochaine version (Cf post de
Marc) la disponibilité d'un tel opérateur de cast ?
* ce cast n'étant pas présent aujourd'hui, peut-on malgré
tout supposer un stockage linéaire et continu des éléments
du vector<T> ? (si ce n'est pas le cas, un pointeur T* est
inutilisable et les joyeux iterators obligeatoires)
* dans le même temps s'il est présent, les opérateurs []
vont devenir confus.

operator const CPoint* () const { return begin(); }


Pareil.


oui, pareil, il suffit d'utiliser &at(0), n'importe quel
developpeur sait le faire.

Et qui permet aussi :
std::vector< CPoint >* pv = new Points ;
delete pv ; // Comportement indéfini.


indéfini parce que vector<> ne définit pas son destructeur
comme virtuel; tu l'as déjà indiqué "Il n'a pas été conçu
pour servir de classe de base."

je m'interroge ici aussi sur ce choix.

Mais vu que le code ne se compile pas avec un compilateur
moderne (et n'a jamais été légal), qu'importe.


sur le fond, je suis d'accord avec toi, "qu'importe" de savoir
si begin, pop_truc ou push_machin se compile, ce n'était pas
la question. qu'importe également de savoir si tu fais exprès
de commenter à coté ou si le point n'était pas suffisamment
clair pour toi.

la question qui reste est: peux-t-on accéder linéairement
aux élements d'un vector (ou vector<> peut-il un decorator)?
tu n'as pas éclairé ma lanterne sur ce point.

Sylvain.


Avatar
Arnaud Debaene
"JM" a écrit dans le message de news:
4507f054$0$25915$



class Points : public vector<CPoint> {


Ce n'est pas en général une bonne idée d'hériter de std::vector.
Il n'a pas été conçu pour servir de classe de base.



Oui mais si ça marche, j'avoue que c'est le principal.
Respecter la norme pour la norme, ce n'est pas mon truc.
En un truc qui la respecte à la lettre, et un autre un peu moins mais
valable et plus lisible, mon choix est vite fait.


vector<CPoint>* pTbl=new Points();
//.......
delete pTbl;

Ceci ne "marche" pas car le destructeur de Points n'est pas appelé lors du
delete.

Arnaud



1 2 3