OVH Cloud OVH Cloud

STL - liste de string et const

11 réponses
Avatar
Brieuc Jeunhomme
Bonjour,

je cherche à réaliser une classe contenant une liste de chaînes
constantes, j'écris donc différentes implémentations, dont pas une
seule ne compile :)

En enlevant tous les const, l'interface est assez simple :

class foo {

public:

list < string > :: iterator begin() { return elements_.begin(); }

list < string > :: iterator end() { return elements_.end(); }

private:

list < string > elements_;

};


Par contre, si je veux déclarer begin et end comme méthodes const, comme
ceci :

list < string > :: iterator end() const { return elements_.end(); }

j'obtiens une erreur de compilation dûe à des conversions de type sur
les arguments 2 et 3 du template d'itération de liste. Y a-t-il une
solution ?

D'autre part, comme les chaînes qui sont placées dans cette classe ne
sont pas censées être modifiées, je préférerais utiliser des
list < const string >, mais dès que j'essaie d'insérer un élément dans
une telle liste, j'obtiens une erreur de compilation :

list < const string > liste;
const string foo( "foo" );
liste.push_back( foo );

Ne passe pas. À la réflexion, ça me paraît normal puisque push_back doit
quelque part appeler l'opérateur d'affectation, ce qui ne devrait
jamais avoir lieu dans le cas d'un objet déclaré constant. Cependant,
j'aimerais empêcher la modification des chaînes contenues dans la
liste. Y a-t-il une autre solution qu'encapsuler ces chaînes dans une
classe ne disposant pas de l'opération de modification ?

--
BBP

10 réponses

1 2
Avatar
Jean-Marc Bourguet
Brieuc Jeunhomme writes:

Par contre, si je veux déclarer begin et end comme méthodes const, comme
ceci :

list < string > :: iterator end() const { return elements_.end(); }


list<string>::const_iterator end() const { return elements_.end(); }

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

Avatar
Fabien LE LEZ
On Thu, 03 Nov 2005 15:21:17 +0100, Brieuc Jeunhomme
:

list < string > :: iterator end() const { return elements_.end(); }


Si ta fonction est const, "elements_" est const lui aussi.
Or tu ne peux pas prendre un iterator sur un conteneur constant.
Tu ne peux prendre qu'un const_iterator.

je préférerais utiliser des list < const string >


Si tu veux un tableau dans lequel tu peux modifier les élements et
ajouter/supprimer des éléments, utilise list<string>.

Si tu veux un tableau dans lequel tu ne peux ni modifier les élements,
ni ajouter/supprimer des éléments, utilise list<string> const.

Si tu veux un tableau dans lequel tu peux ajouter/supprimer des
éléments mais pas les modifier, ce n'est pas possible : tu peux
toujours modifier un élement, ne serait-ce qu'en supprimant l'élément
puis en insérant le nouveau à la place.

Si tu veux un tableau dans lequel tu peux modifier les éléments
existants mais pas en ajouter ni en supprimer, tu vas devoir faire ton
propre conteneur, car à ma connaissance ça n'existe pas dans les
conteneurs standard.

Avatar
Alexandre
Si tu veux un tableau dans lequel tu peux modifier les éléments
existants mais pas en ajouter ni en supprimer, tu vas devoir faire ton
propre conteneur, car à ma connaissance ça n'existe pas dans les
conteneurs standard.



ça devrait pouvoir se faire assez rapidement en héritant d'un conteneur
standard (ou en l'utilisant en template). Tiens, ça me donne une idée pour
un sujet de TD, merci ;-)

Avatar
Fabien LE LEZ
On Thu, 3 Nov 2005 18:21:17 +0100, "Alexandre"
:

en héritant d'un conteneur standard


Mauvais plan : les conteneurs de la STL ne sont pas prévus pour servir
de classes de base.

De plus, hériter d'une classe pour enlever des fonctions ne me paraît
pas une bonne idée.

Par contre, pondre une classe contenant un std::vector<> et une
interface publique simplifiée (begin(), end(), at(), et c'est tout)
est effectivement trivial.

Tiens, ça me donne une idée pour
un sujet de TD, merci ;-)


En prime, tu pourras mettre 0 à toute personne qui proposerait de
baser un tel conteneur sur std::list<>...

Avatar
Jean-Marc Bourguet
"Alexandre" writes:

Si tu veux un tableau dans lequel tu peux modifier les
éléments existants mais pas en ajouter ni en supprimer,
tu vas devoir faire ton propre conteneur, car à ma
connaissance ça n'existe pas dans les conteneurs
standard.



ça devrait pouvoir se faire assez rapidement en héritant
d'un conteneur standard (ou en l'utilisant en
template). Tiens, ça me donne une idée pour un sujet de
TD, merci ;-)


Tant qu'on ne veut pas les détruire polymorphiquement, quel
est le problème? (Et à voir l'objectif, je suppose que
l'héritage se fera de manière privée ou bien c'est un peu
trop facile de subvertir, donc à priori pas de destruction
polymorphique).

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


Avatar
Fabien LE LEZ
On 03 Nov 2005 21:15:03 +0100, Jean-Marc Bourguet :

Tant qu'on ne veut pas les détruire polymorphiquement, quel
est le problème?


Typiquement, on croit avoir telle ou telle assurance sur l'utilisation
d'une classe, et puis quelques mois (ou années) plus tard, le projet
évolue, et on se retrouve avec des bugs inexpliqués.

D'autre part, il y a en C++ un certain nombre de "règles absolues",
qu'on doit suivre tant qu'on n'a pas assez d'expérience sur le sujet
pour savoir quand on peut les détourner sans risque. D'autant que
connaître les conditions de destruction d'un objet en C++ n'est pas si
évident que ça.

J'ajoute que l'absence de destructeur virtuel peut constituer une
indication de l'auteur de la classe qu'il ne faut pas en dériver.

Avatar
kanze
Fabien LE LEZ wrote:
On 03 Nov 2005 21:15:03 +0100, Jean-Marc Bourguet :

Tant qu'on ne veut pas les détruire polymorphiquement, quel
est le problème?


Typiquement, on croit avoir telle ou telle assurance sur
l'utilisation d'une classe, et puis quelques mois (ou années)
plus tard, le projet évolue, et on se retrouve avec des bugs
inexpliqués.


Oui, mais ne ne peut jamais tout prévoir, ni permettre. L'idée
qu'il faut prévenir des emplois qu'on ne veut pas supporter ne
tient pas la route. Déjà, la norme en a plein d'exemples des
classes qui dérivent d'une autre classe, sans destructeur
virtuel.

Ici, Jean-Marc a dit que l'héritage serait sans doute privé. On
pourrait, évidemment, au moyen des conversions explicites,
parvenir à obtenir un pointeur à la classe de base, pour faire
le delete, mais il faut bien le vouloir.

D'autre part, il y a en C++ un certain nombre de "règles
absolues", qu'on doit suivre tant qu'on n'a pas assez
d'expérience sur le sujet pour savoir quand on peut les
détourner sans risque.


La première règle absolue, c'est de lire la documentation d'une
classe avant de s'en servir, et de se conformer à cette
documentation. Sinon, c'est foutu d'avance.

D'autant que connaître les conditions de destruction d'un
objet en C++ n'est pas si évident que ça.


Mais le problème ne se présente qu'en cas de delete. Tous les
autres cas de destruction sont bien défini. Et les conditions
d'un delete, c'est bien le programmeur qui les définit ; c'est
une opération explicite.

J'ajoute que l'absence de destructeur virtuel peut constituer
une indication de l'auteur de la classe qu'il ne faut pas en
dériver.


Je ne crois pas que les collections de la norme ont été conçues
pour servir des classes de bases. Je serais d'ailleurs très
scéptique d'une application qui en dérivait publiquement (mais
j'imagine qu'il y a des cas où c'est justifié). La dérivation
privée, en revanche, est un détail de l'implémentation, qui me
gèrerait beaucoup moins, voire même pas du tout.

--
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
Jean-Marc Bourguet
Fabien LE LEZ writes:

D'autre part, il y a en C++ un certain nombre de "règles absolues",
qu'on doit suivre tant qu'on n'a pas assez d'expérience sur le sujet
pour savoir quand on peut les détourner sans risque. D'autant que
connaître les conditions de destruction d'un objet en C++ n'est pas
si évident que ça.


Je crois que notre point de desaccord fondamental est ici. Il y a un
certains nombres de regles qu'il vaut mieux appliquer avant de savoir
quelles en sont les limites. Mais au strict minimum il faut savoir
que ce ne sont pas des regles absolues et suivant les cas certaines
limites sont faciles a connaitre.

Le manque de destructeur est dangereux quand on risque de detruire
l'objet a partir d'une classe de base. Quand la classe de
Dans le cas present ou il s'agit de faire un conteneur avec des
contraintes, il me semble evident que si l'heritage est utilise
(personnellement, j'aurais tendance a plutot utiliser la delegation
que l'heritage) c'est un heritage prive ou protege (sinon les
contraintes sont trop facilement contournables). Dans ce cas, d'un
heritage prive, les risques qu'on detruise l'objet a partir de la base
sont minimes (surtout quand la base n'a aucun membres virtuels).

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

Avatar
Alexandre
Mauvais plan : les conteneurs de la STL ne sont pas prévus pour servir
de classes de base.


à cause du destructeur non virtuel, bien sûr. Mais en héritant en mode
privé...


De plus, hériter d'une classe pour enlever des fonctions ne me paraît
pas une bonne idée.


ben c'était le pb de départ : définir un conteneur où l'on ne peut pas
ajouter d'éléments... Plutôt que de refaire la roue...


Par contre, pondre une classe contenant un std::vector<> et une
interface publique simplifiée (begin(), end(), at(), et c'est tout)
est effectivement trivial.


au lieu de contenir, on peut hériter en mode privé. Ca revient (presque) au
même.


Tiens, ça me donne une idée pour
un sujet de TD, merci ;-)


En prime, tu pourras mettre 0 à toute personne qui proposerait de
baser un tel conteneur sur std::list<>...


bien sur ;-) et s'il prend std::map ? ;-)


Avatar
Alexandre
Typiquement, on croit avoir telle ou telle assurance sur l'utilisation
d'une classe, et puis quelques mois (ou années) plus tard, le projet
évolue, et on se retrouve avec des bugs inexpliqués.


ben si on hérite en mode privé, on n'utilisera de toute façon pas la classe
via une interface...


D'autre part, il y a en C++ un certain nombre de "règles absolues",
qu'on doit suivre tant qu'on n'a pas assez d'expérience sur le sujet
pour savoir quand on peut les détourner sans risque. D'autant que
connaître les conditions de destruction d'un objet en C++ n'est pas si
évident que ça.


dans le cas précis (héritage de std::vector en mode privé) pas de soucis.


J'ajoute que l'absence de destructeur virtuel peut constituer une
indication de l'auteur de la classe qu'il ne faut pas en dériver.


en mode public, je suis d'accord...




1 2