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

5 réponses

1 2 3
Avatar
Sylvain
Arnaud Debaene wrote on 13/09/2006 22:54:

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, il est autorisé d'essayer de comprendre - je ne dis pas que tu
ne le fais pas, mais ici tu nous repasses une soupe déjà tiède.

James - le taquin - s'amuse à nous inventer une écriture sciemment
biaisée et qui ne viendrait à l'idée de personne: je créé un machin mais
na je le mets dans un truc et ça marchera pas.

soit, mais
a) qui impose cette écriture là ?
b) pourquoi oublie-t-il d'être honnête et d'indiquer que cela ne
fonctionne pas du seul fait du codage (ou dit "feature"?) de la STL ?
(ne pas déclarer le destructeur virtuel); soit il nous gratifie d'un
pudique "Il n'a pas été conçu pour servir de classe de base"
c) qu'est-ce qui empêche de modifier (corriger?) la définition de
vector?, c'est pas open-source ?, c'est ferme-ta-gueule ??

Sylvain.




Avatar
kanze
Sylvain wrote:
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 ...


Il a parlé de la nécessité de changer des interfaces. Ce qui
suppose que la possibilité existe.

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


L'objet décoré, évidemment, c'est l'interface ancienne qui veut
des CPoint* ; on lui décore d'une interface qui prend des
std::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 ?


C'est vrai pour la plupart de la STL. Stepanov n'aimait pas
l'héritage.

Note bien que ce n'est pas une règle absolue, et qu'il y a sans
doute des cas. N'empèche que quand on commence (comme c'était le
cas du posteur initial), c'est mieux de rester dans les droites
lignes du classique jusqu'à de s'y vraiment connaître.

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.


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


Selon le langage, c'est un comportement indéfini. Autant que je
sache, Microsoft ne l'a pas défini non plus. Donc, s'il marche,
c'est un coup d'hazard. (Je pourrais bien imaginer qu'il marche
avec optimisation, par exemple, et non sans.)

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


La STL fait partie du langage. C'est une bibliothèque livrée
avec le compilateur. Dans certains cas, on peut le remplacer (et
les versions actuelles de Sun CC sont livrées avec deux versions
distinctes de la bibliothèque standard), mais si on ne précise
pas autrement, il faut assumer la version livrée avec le
compilateur.

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


C'est comme la reste du langage. Il y a une définition de la
bibliothèque. Tu la respectes, et il ne doit pas avoir de
problème. Tu t'amuses à jouer le fou avec des comportements
indéfini, et de fouillir dedans pour exploiter des
particularités de l'implémentation, tu vas avoir des problèmes.

Depuis le début (chez Stepanov, avant même que le comité C++ l'a
régardé), des fonctions begin() et end() ont renvoyé des
iterator (ou des const_iterator, pour leur version const). Un
type opaque garantit à supporter un certain nombre d'opérations.
C'est une chose qui n'a pas changé depuis quelque chose comme 15
ans. C'est une chose qui n'a pas changé depuis que le premier
compilateur C++ a été livré avec la STL. Ça fait partie des
principes de base de la STL ; je dirais même que la définition
des itérateurs, c'est ce qui caractèrise la STL le plus par
rapport à d'autres bibliothèques dont je me suis servi.

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


Ça fait partie du langage. C'est défini par le même document que
la reste du langage, ISO 14882.

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 le réformule : est-ce que c'est possible de changer le type
d'une donnée, en général, sans changer les déclarations des
interfaces qui l'utilisent. La réponse est évidemment non, au
moins que les interfaces soient définies comme templates. Le C++
est un langage au typage statique.

- 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


Là, on est à peu près d'accord. Une seule fois, je n'en sais
rien, mais une fois seulement dans chaque décorateur de classe
« legacy ».

- car le point est bcp plus:
* vector<> jouant (déjà) un role de décorateur:


vector<> n'est pas un décorateur. Je ne sais pas où tu trouves
ça. Il n'a rien du modèle décorateur.

peux-t-on attendre d'une prochaine version (Cf post de
Marc) la disponibilité d'un tel opérateur de cast ?


Certainement pas. Ça serait contre toutes les règles de la bonne
programmation. L'évolution du C++, ça a été toujours dans le
sens de la génie logicielle, de rendre la programmation plus
simple et plus sûre, et non d'augmenter les possibilités
d'erreurs subtiles à l'infini.

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


C'est garantie depuie la version 2003 de la norme. Afin de
facilité l'interface avec des bibliothèques « legacy ».

* dans le même temps s'il est présent, les opérateurs []
vont devenir confus.


Comment, confus. Il y a deux opérateurs [], un const, et un non
const. Comment veux-tu qu'il devient « 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."


Tout à fait.

je m'interroge ici aussi sur ce choix.


Tu n'es probablement pas le seul. Disons que dans la contexte
totale de la STL, c'est un choix tout à fait logique. Si la
philosophie de base de la STL était un bon choix, c'est une
autre question : parmi les bibliothèques de ce genre que j'ai
eu l'occasion d'utiliser, c'est sans doute le moins bien du
point de vue de la conception, au moins à mon avis. (J'ai
beaucoup aimé les composants Booch, ou OSE.) En revanche, c'est
sûrement la plus complète, et de loin. Aussi, c'est la seule (je
crois) qui a été proposée à la normalisation.

[...]
la question qui reste est: peux-t-on accéder linéairement
aux élements d'un vector


Oui. Ce n'était pas garantie dans la version initiale, mais on a
ajouté la garantie par la suite pour rendre son utilisation
possible avec des interfaces anciennes. C'est toujours un peu
délicat, parce qu'il faut vérifier bien que le vecteur n'est pas
vide avant d'appeler [], mais c'est suffisant pour résoudre le
problème visé.

(ou vector<> peut-il un decorator)?


Je ne suis pas sûr de comprendre le sens exacte ici, mais en
gros, vector<> n'a rien à voir avec le modèle décorateur.

--
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
Cyrille wrote:
Franck Branjonneau wrote on 13/09/2006 19:37:
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 ?


pour rien.
je ne suis pas payé au caractère, ça doit m'empêcher d'appréc ier.


Euh, ça permet justement d'économiser les efforts en évitant
d'avoir à modifier le code existant... Et d'économiser des
caractères si f est appelé relativement souvent.


J'avoue ne pas comprendre ni l'un ni l'autre des critères :

-- Si on passe de T[] à std::vector<T>, on est bien en train de
modifier le code existant, non ? Et il vaut bien mieux que
le code soit homogène, et non une miche-mache imbitable des
idiomes différents, et qu'on ne sait jamais exactement ce
auquel on a à faire.

-- Mon prof d'anglais, au lycée, disait toujours que « good
writing is clear and concise ». Je crois que les mêmes
critères s'appliquent au code. Et note bien qu'il y a deux
critères, et que « consise » ne signifie pas « le plus
petit nombre de caractères » non plus. L'important dans le
code, ce n'est pas le nombre de caractères, c'est la
lisabilité. Des conversions implicites, par exemple, en
dehors de certains cas spécifiques, nuisent énormement à la
lisabilité, et sont à éviter. (En fait, on pourrait aussi
dire que l'utilisation de T* pour passer un tableau, c'est
tout sauf clair.)

--
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
Franck Branjonneau
"kanze" écrivait:

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]); }




je ne suis pas payé au caractère, ça doit m'empêcher d'apprécier.


Euh, ça permet justement d'économiser les efforts en évitant
d'avoir à modifier le code existant... Et d'économiser des
caractères si f est appelé relativement souvent.


J'avoue ne pas comprendre ni l'un ni l'autre des critères :

-- Si on passe de T[] à std::vector<T>, on est bien en train de
modifier le code existant, non ? Et il vaut bien mieux que
le code soit homogène, et non une miche-mache imbitable des
idiomes différents, et qu'on ne sait jamais exactement ce
auquel on a à faire.


Oui. Je répondais à « ne pas tout changer dans un premier temps ».

--
Franck Branjonneau








Avatar
kanze
Franck Branjonneau wrote:
"kanze" écrivait:

-- Si on passe de T[] à std::vector<T>, on est bien en train de
modifier le code existant, non ? Et il vaut bien mieux que
le code soit homogène, et non une miche-mache imbitable des
idiomes différents, et qu'on ne sait jamais exactement ce
auquel on a à faire.


Oui. Je répondais à « ne pas tout changer dans un premier
temps ».


Je comprends. Mais il veut changer une abstraction fondamentale
de son application. Il ne l'avait pas encapsulé, comme on fait
d'habitude. C'est donc inévitable qu'il ait beaucoup à changer.
C'est le prix de ne pas avoir encapsulé la représentation avant.
(Aussi, s'il change tout en std::vector<T>, il risque d'avoir le
même problème par la suite. À mon avis, ce serait une bonne
occasion d'implémenter une encapsulation -- une classe à lui
qui présente l'interface voulue, et qui est implémenté en termes
de std::vector<T>.)

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


1 2 3