OVH Cloud OVH Cloud

const_cast

22 réponses
Avatar
meow
J'ai donc commenc=E9 =E0 =E9tudier la question des casts, et je commence
par le const_cast.

1=2E) J'ai lu que cet 'op=E9rateur' ne fonctionnait que sur les types :
pointeurs, r=E9f=E9rences, et pointeurs sur membre...
a.) c'est quoi un pointeur sur membre
b.) pourquoi a t'on d=E9cid=E9 que =E7a fonctionnerait pas sur des
types tout betes ? o=F9 est le probl=E8me dans l'ecriture const_cast<A> ?

2=2E) J'ai ensuite test=E9 sur un exemple :

class A {
public:
void f(){std::cout<<"A::f()"<<std::endl;};
A(){;}; // (*)
};

int main()
{
const A a;
const_cast<A&>(a).f();
}

a.) la notation const_cast<A&> pour 'moralement' signifier
const_cast<A> me trouble un peu... y'a un truc que je vois pas ?
b.) si je commente la ligne marqu=E9e d'une at=E9risque (*), =E7a ne
compile pas : g++ pr=E9tend que je n'initialise pas mon "const A a;"...
Y'a pas de constructeur (vide) par d=E9faut ? M'aurait-t'on menti ?

10 réponses

1 2 3
Avatar
Fabien LE LEZ
On 9 Dec 2005 00:51:46 -0800, "kanze" :

Exceptionnellement, dans certains cas où aucune ambiguïté n'est
possible, il est aussi permis de mettre le const avant, par
exemple :
const A* p ;


Y a-t-il des autres cas ?

Avatar
kanze
wrote:
Il y a aussi une exception en ce qui concerne les objets
const -- si l'objet a un type classe, et contient au moins
un membre mutable, on peut le modifier légalement, même s'il
est défini const.


Tiens, je sais pas ça. Et j'arrive pas à trouver où c'est
défini dans la norme. Tout ce que je vois, c'est :

Except that any class member declared mutable (7.1.1) can be
modified, any attempt to modify a const object during its
lifetime (3.8) results in undefined behavior.


Je vois qu'on peut modifier le membre mutable, mais rien du
reste de l'objet.


En effet. Il y a même un exemple qui dit le contraire.

Je me suis basé sur mes souvenirs de ce qu'on avait discuté et
voté -- j'étais présent quand on a adopté mutable. Alors :
-- ou la texte de la norme reflète mal ce qu'on a voté,
-- ou on a changé les règles par la suite,
-- ou simplement je me souviens incorrectement.
C'est probablement ce dernier -- je suis sûr qu'on a discuté
cette possibilité, mais je suis moins sûr en ce qui concerne ce
qui est arrivé à la vote finale.

(Dans la pratique, la modification marchera, parce que
l'implémentation n'arrivera pas à mettre l'objet en ROM, tout en
respectant les autres contraints sur l'orgainisation en mémoire.
De même que dans la pratique, tout objet ayant une
initialisation dynamique serait modifiable, parce que les
systèmes actuels n'ont pas de possibilité de protéger en
écriture après l'initialisation. Mais ce sont des considérations
pragmatique basées sur des technologies d'implémentation
d'aujourd'hui. C'est mieux de ne pas y compter, pour ne pas être
pris au dépourvu si la technologie évolve.)

--
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
meow
Je ne comprends pas ce que tu essaies de dire.
Une connerie, très probablement... Mais en gros je partais du principe

qu'en déclarant quelque chose const, je laisse au compilo
l'opportunité de ne meme pas reserver de mémoire pour tout ou partie
de ce quelque chose. Comme dans "const int i=2", où le compilo va se
sentir autorisé à remplacer tous mes "i" par des "2". Bref, dans ce
cas, c'est pas physiquement possible de changer la valeur dans la
mesure où physiquement justement, elle n'a meme pas d'emplacement...
Et je remarquais que lorsqu'on pointe sur un objet const, bein c'est
qu'on a forcément une adresse mémoire où trouver l'objet. Qu'il a
donc bien un amplacement physique, et que meme si c'est mal, bein on
peut le modifier quitte à dire qu'on sait ce qu'on fait (via
const_cast).
En revanche, on peut toujours appliquer un const_cast à cette
expression, ce qui donne une nouvelle expression, qui lui n'est
pas forcément const.
Familièrement, on peut dire que tu utilises
const_cast pour dire que l'objet en question n'est pas const,
même si l'expression l'est. Et si dans ce cas, l'objet est
const, tu as menti au compilateur.
Je ne suis pas certain de comprendre... Mais c'est peut etre lié à la

définition de lvalue. Au risque de passer pour un abruti je vais quand
m'assurer d'avoir bien compris ce qu'était une lvalue : "une
expression quelconque qui ne désigne pas dirrectement un objet mais
qui une fois résolue désigne un objet"
Par exemple :
A *p=new std::vector<A>();A *q=new std::vector<A>(); *p[1]=*q[1]; //
*p[1] et *q[1] sont des lvalue.
Question : dans A *p=new A();A *q=new A();*p=*q; qu'en est-il de *p et
*q ? Ce sont des expressions qui se résolvent en objets de type A,
mais à la base ce sont de pointeurs, et donc aussi des objets...

La notation const A, A const :
Dans ma 2nde édition Stoustrup au chapitre des const (p67) on ne parle
pas de la notation "int const bidule"...

A f() const ; // C'est la fonction qui est const
Ce qui signifie qu'elle doit laisser l'objet inchangé, non ?


Avatar
James Kanze
Fabien LE LEZ wrote:
On 9 Dec 2005 00:51:46 -0800, "kanze" :


Exceptionnellement, dans certains cas où aucune ambiguïté
n'est possible, il est aussi permis de mettre le const avant,
par exemple :



const A* p ;



Y a-t-il des autres cas ?


const B* p ;
const C* p ;

... :-)

Ça dépend de ce que tu considère un cas différent.

En fait, comme tu sais, ce n'est pas tellement qu'il y a une
« permission » spéciale pour mettre le const devant. C'est qu'il
y a une contexte où l'ordre n'a pas d'importance. On peut écrire
donc également :

const unsigned long i ;
const long unsigned i ;
unsigned const long i ;
unsigned long const i ;
long const unsigned i ;
long unsigned const i ;

Lequel on choisit est une question de goût, mais dans mon propre
code, je n'utilise que « unsigned long const », au moins que les
règles du codage du client imposent autre chose. Et même là :
j'accepterais silencieusement « const unsigned long », mais je
râlerais contre une règle qui imposait autre chose qu'une de ces
deux.

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


Avatar
James Kanze
meow wrote:
Je ne comprends pas ce que tu essaies de dire.



Une connerie, très probablement... Mais en gros je partais du
principe qu'en déclarant quelque chose const, je laisse au
compilo l'opportunité de ne meme pas reserver de mémoire pour
tout ou partie de ce quelque chose. Comme dans "const int
i=2", où le compilo va se sentir autorisé à remplacer tous mes
"i" par des "2".


Tout à fait. Quand l'objet est const (et non volatile), sa
valeur ne peut pas changer. Le compilateur a le droit
d'exploiter ce fait comme il veut.

Bref, dans ce cas, c'est pas physiquement possible de changer
la valeur dans la mesure où physiquement justement, elle n'a
meme pas d'emplacement... Et je remarquais que lorsqu'on
pointe sur un objet const, bien c'est qu'on a forcément une
adresse mémoire où trouver l'objet. Qu'il a donc bien un
amplacement physique, et que meme si c'est mal, bein on peut
le modifier quitte à dire qu'on sait ce qu'on fait (via
const_cast).


Un objet const reste un objet, au sens du C++. Il a donc une
adresse. C'est au compilateur de se débrouiller pour que le
programme ait la bonne sémantique (en supposant qu'il n'a pas de
comportement indéfini). S'il a besoin réelement de l'objet, il
va le générer. Sinon, probablement pas. S'il a besoin dépend de
ce qu'on fait avec l'objet, mais aussi du niveau de
l'optimisation du compilateur. C'est donc difficile à faire des
declarations générales.

En revanche, on peut toujours appliquer un const_cast à cette
expression, ce qui donne une nouvelle expression, qui lui
n'est pas forcément const. Familièrement, on peut dire que tu
utilises const_cast pour dire que l'objet en question n'est
pas const, même si l'expression l'est. Et si dans ce cas,
l'objet est const, tu as menti au compilateur.



Je ne suis pas certain de comprendre... Mais c'est peut etre
lié à la définition de lvalue. Au risque de passer pour un
abruti je vais quand m'assurer d'avoir bien compris ce
qu'était une lvalue : "une expression quelconque qui ne
désigne pas dirrectement un objet mais qui une fois résolue
désigne un objet"


C'est plus simple que ça. Si l'expression désigne un objet,
c'est un lvalue. Sinon, non. (Grosso modo -- la définition
formelle dans la norme est beaucoup plus compliquée, mais elle
revient à peu près à ça.) Donc, donné :

int i ;
int j ;

j = 1 ; // i et un lvalue, 1 non...
i = j ; // i et j sont des lvalues...

Dans les deux cas, l'expression complète est aussi un lvalue
(qui désigne l'objet à droit de l'affectation). Pour chaque
expression, et chaque sous-expression, la norme dit si c'est un
lvalue ou non. Et c'est ça, en fait, la définition formelle.
Mais en gros, si l'expression réfère à un objet, ou modifie un
objet, et la valeur de l'expression est la valeur de cet objet
après la modification, l'expression est un lvalue.

Dans le cas qui nous intéresse, le resultat d'une expression *p
est toujours un lvalue (à supposer que l'expression est légale,
c-à-d p est un pointeur valid). Le nom d'un objet est aussi un
lvalue, mais dans ce cas-là, si l'expression lvalue est const,
c'est que l'objet est const. Tandis qu'il est assez facile à
obtenir des pointeurs ou des références à des const quand
l'objet même ne l'est pas.

Par exemple :


A *p=new std::vector<A>();A *q=new std::vector<A>(); *p[1]=*q[1]; //
*p[1] et *q[1] sont des lvalue.


Pas tout à fait -- ce que tu as écrit n'est pas du C++ légal,
donc on ne peut rien dire:-). Mais en général...

-- Si on parle de l'opérateur [] de base, il est défini en
termes de l'opérateur* -- l'expression a[b] est,
formellement, rien qu'une autre façon d'écrire *(a+b). Le
résultat est donc toujours un lvalue.

-- Si on parle d'un opérateur [] surchargé, comme celui de
std::vector, c'est en fait un appel de fonction. Qui obéit
aux règles des appels de fonction, c-à-d que c'est un lvalue
si et seulement si le type de rétour de la fonction est une
référence. (Ce qui est le cas de std::vector<>::operator[].)

Mais je suis curieux à ce que tu pensais faire dans ton exemple.
Parce qu'il n'a pas de sens en C++ -- si je fais new d'un
std::vector<A>, je ne peux pas en affecter le résultat à un A*.
Et new std::vector<A>() alloue un tableau vide. Et c'est plutôt
rarissime d'allouer les std::vector dynamiquement. Et
finalement, dans l'expression *p[1], c'est l'indexation qui a
précédence, et l'expression n'a du sens que si p[1] renvoie un
pointeur.

Si le but est simplement d'avoir un tableau, il y a plusieurs
façons, mais la plus courante, de loin, c'est :

std::vector< A > tableau( dim ) ;

ou simplement :

std::vector< A > tableau ;

quand on ne sait pas d'avance la taille (on remplit le tableau
avec des push_back par la suite), ou :

std::vector< A > tableau( debut, fin ) ;

quand on a une suite de valeurs (définie par les itérateurs
debut et fin) qui doivent servir d'initialisation.

Question : dans A *p=new A();A *q=new A();*p=*q; qu'en est-il
de *p et *q ?


La déréferencement d'un pointeur donne toujours un lvalue.

Ce sont des expressions qui se résolvent en objets de type A,
mais à la base ce sont de pointeurs, et donc aussi des
objets...


Ce sont des expressions qui désignent des objets concrets. À
l'opposé des choses du genre « 2 », ou « i + 1 », où il n'y a
pas d'objet, mais simplement une valeur. (Mais la distinction
est un peu estompée dans le cas des classes -- une valeur de
type classe est toujours un objet, même quand elle n'est pas un
lvalue.)

La notation const A, A const :


Dans ma 2nde édition Stoustrup au chapitre des const (p67) on
ne parle pas de la notation "int const bidule"...


A f() const ; // C'est la fonction qui est const



Ce qui signifie qu'elle doit laisser l'objet inchangé, non ?


À peu près, pour la bonne définition d'« inchangé ».
Techniquement, ça signifie 1) que le pointeur this dans la
fonction aura le type A const*, et non A*, et 2) qu'on peut
appeler la fonction sur un objet const.

Au niveau du langage et du compilateur, si l'objet contient des
pointeurs, il n'y a aucune manière à distinguer s'il s'agit de
l'implémentation d'une rélation, ou si ce qui est pointé fait
logiquement partie de l'objet. Par convention (quasiment
universelle aujourd'hui), on ne declare const que des fonctions
qui ne change pas la valeur conceptuelle de l'objet, même si, du
fait qu'il y un pointeur, le compilateur permettrait le const.
(À titre d'exemple, une classe String qui contient un char*. Une
fonction membre const pourrait, selon le langage et le
compilateur, modifier des données dans le tableau désigné par ce
pointeur. Par convention, en revanche, une fonction qui les
modifie ne serait pas déclarée const.)

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


Avatar
meow
Tout à fait. Quand l'objet est const (et non volatile)
Oh, tiens, bein tant qu'à y etre, qu'est-ce que ça signifie

exactement "volatile" ? D'après ce que j'en comprends ça sert à
demander au compilo de laisser tomber toutes les optimisations sur cet
objet...

Pas tout à fait -- ce que tu as écrit n'est pas du C++ légal,
Je supposes que ce qui manque pour que ce soit légal, c'est une

initialisation de mes vectors avec, disons 2 valeurs par défaut ?

Mais je suis curieux à ce que tu pensais faire dans ton exemple.
Rien de particulier, c'etait juste histoire d'avoir des pointeurs sur

des trucs un peu compliqués... Mais ça tombait plutot bien, comme
ça, ça t'a donné l'occasion de me rappeller que la notation []
était quelque peu trompeuse ;)

contient au moins un membre mutable, on peut le modifier
Par curiosité : quel est l'interret de "mutable" ?


Avatar
Jean-Marc Bourguet
"meow" writes:

Tout à fait. Quand l'objet est const (et non volatile)


Oh, tiens, bein tant qu'à y etre, qu'est-ce que ça signifie
exactement "volatile" ? D'après ce que j'en comprends ça sert à
demander au compilo de laisser tomber toutes les optimisations sur
cet objet...


Tant que tu ne travailles pas directemenent avec le hard, volatile a
peut d'effets (le plus important etant que les variables volatiles
sont plus ou moins fixees apres un longjmp, mais faire des longjmp en
C++, c'est pas vraiment conseille...)

contient au moins un membre mutable, on peut le modifier
Par curiosité : quel est l'interret de "mutable" ?



Les membres mutables peuvent etre modifies meme quand l'objet est
const (c'est donc l'inverse des membres const qui ne peuvent pas etre
modifie meme quand l'objet n'est pas const).

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
Anthony Fleury
Tout à fait. Quand l'objet est const (et non volatile)


Oh, tiens, bein tant qu'à y etre, qu'est-ce que ça signifie
exactement "volatile" ? D'après ce que j'en comprends ça sert à
demander au compilo de laisser tomber toutes les optimisations sur cet
objet...


En fait, volatile veut dire qu'une variable peut être modifiée par autre
chose que les lignes de code que le compilateur va analyser. En gros, si
tu as un code qui est :

int i = 1;

[...] /* pleins de trucs sans intervention de i */

f(i);

Ici, si la variable n'est pas volatile, le compilateur ne serait pas
obligé de relire la valeur de i pour l'envoyer à f() d'une part, et de
deuxième part, la valeur de i après l'instruction volatile int i = 1;
n'est pas forcément 1 au moment du ';', le tout c'est que le
comportement _visible_ du programme ne soit pas modifié, et donc qu'au
moment de l'appel de f() [si cet appel est la première utilisation de i
qui suit la définition] i soit bien égal à 1.

Par contre, si on définit cette variable comme volatile, la machine
abstraite ne doit pas se fier à ce qu'elle déduit du code pour voir si
le comportement est changé par une optimisation ou autre, et _doit_
obligatoirement produire un code qui d'une part va mettre à jour la
valeur de i au moment de la définition, et de seconde part va relire la
valeur de i à l'appel de f().


contient au moins un membre mutable, on peut le modifier


Par curiosité : quel est l'interret de "mutable" ?



En C++, const a une vraie existance et est bien utilisable. Donc lorsque
nous devons déclarer une fonction qui ne modifie pas la classe, elle
doit être déclaré const.
Cependant, dans certains cas, on doit avoir à modifier une variable de
la classe, qui ne change pas vraiment cette classe et ne modifie pas
notre « objet » au sens où nous le voyons (par exemple un compteur
d'accès à une instance d'une classe ou autre). Si nous voulions faire
sans mutable, il faudrait se balader uniquement avec des variables non
const pour pouvoir modifier cette valeur. Là, avec mutable, on peut
modifier la valeur de la variable même lorsque la classe est const et
s'assurer que notre « objet » est bien constant.

--
Anthony Fleury


Avatar
kanze
meow wrote:
Tout à fait. Quand l'objet est const (et non volatile)


Oh, tiens, bein tant qu'à y etre, qu'est-ce que ça signifie
exactement "volatile" ? D'après ce que j'en comprends ça sert
à demander au compilo de laisser tomber toutes les
optimisations sur cet objet...


Rien dans les faits:-).

Le mot clé a été introduit pour signaler au compilateur d'un
objet peut changer de valeur d'une façon « extra-linguale » : un
dehors du language. L'exemple typique, c'est une porte d'entrée
mappée en mémoire qui renvoie l'heure temps-réel. C-à-d que le
compilateur doit toujours supposer que la valeur ait pu changer,
et le rélire, même si l'objet est const ou que le compilateur
puisse « prouver » que la valeur n'a pas pû changer.

Dans la pratique, si tu écris des programmes qui tournent en
mode utilisateur sous Unix ou sous Windows, il n'y a aucun cas
où tu risques de l'utiliser ou de le voir (sauf peut-être dans
un handleur de signal, et encore).

Pas tout à fait -- ce que tu as écrit n'est pas du C++
légal,


Je supposes que ce qui manque pour que ce soit légal, c'est
une initialisation de mes vectors avec, disons 2 valeurs par
défaut ?


Non. Les types ne collaient pas. Alors, ça ne faisait aucun
sens. Prenons :

A *p = new std::vector< A >() ;

D'abord, tu alloues un objet de type std::vector<A>
dynamiquement. On ne sait pas trop pourquoi, d'ailleurs -- je
crois que je n'ai jamais alloué un std::vector dynamiquement.
Mais tu en alloues *un* *seul* objet, de type std::vector<A> ;
le type de l'expression new est donc std::vector<A>*, c-à-d un
pointeur à cet objet (avec le type pointeur à type d'objet).
Ensuite, tu essaies de t'en servir pour initialiser un A*. Ça ne
va pas du tout -- un std::vector<A> n'est pas un A, et donc un
std::vector<A>* ne peut pas servir à initialiser un A*.

Ensuite, tu fais la même chose pour q (avec les mêmes
problèmes), et finalement, tu écris :

*p[1] = *q[1] ;

Alors, analysons cette expression (parce que dans cette
expression, il y a bien certaines bizarreries du C++). Si on
prend la côté droite, *q[1], les règles de précédance fait que
c'est l'équivalent de *(q[1]). La première opération est donc
q[1]. Et là, on tombe sur une anomalie bizarre de C++. Le type
de q (tel que tu l'as declaré), c'est A*. Un pointeur donc (et
non un type de classe), alors, c'est l'opérateur prédéfini []
qui sert, et non un surcharge. Or, la définition de l'opérateur
prédéfini [] est : l'expression a[b] équivaut exactement *(a+b).
C-à-d ici *(p+1). Ce qui n'a du sens que si on a alloué
plusieurs objets avec le new, et non un seul. Si, par exemple,
j'avais écrit « A* q = new A[ 42 ] ; ». Et le résultat de
l'expression, c'est un objet de type A. Ce qui nous amène à la
reste de la sous-expression : qu'est-ce que veut dire
l'opérateur * sur un objet de type A.

J'ai nettement l'impression que tu confonds les deux types de
tableau en C++, les std::vector<A> et les A[]. Mais il y a plus
que ça qui n'y colle pas. Ce que tu as écrit n'a pas de sens en
C++, mais je n'arrive pas à comprendre ce que tu croyais ou
voulais faire, pour saisir completement ce que tu as mal compris.

Mais je suis curieux à ce que tu pensais faire dans ton
exemple.


Rien de particulier, c'etait juste histoire d'avoir des
pointeurs sur des trucs un peu compliqués... Mais ça tombait
plutot bien, comme ça, ça t'a donné l'occasion de me rappeller
que la notation [] était quelque peu trompeuse ;)


Quelque peu ?

En revanche, comme la plupart des opérateurs, on peut le
surcharger, ce qu'a fait std::vector. Dans ce cas-là, c'est
formellement une fonction, mais on peut se débrouiller avec la
fonction pour qu'il ait une sémantique pas trop étonnant. Si ton
but était de créer deux tableaux, puis d'en affecter un élément
d'un à un élément de l'autre :

std::vector< A > p( 10 ) ;
std::vector< A > q( 10 ) ;
p[ 1 ] = q[ 1 ] ;

Sans new, et sans pointeurs.

En C++, on utiliser les pointeurs pour exprimer les rélations,
mais rarement autrement. C'est un peu une exagération, parce
qu'il y a bien d'autres utilisations des pointeurs. Mais quand
on commence, c'est bien l'utilisation pour exprimer une rélation
qui domine. Surtout : en C++, l'aggrégation ne se sert pas des
pointeurs, au moins directement. (Il y a sans doute au moins un
pointeur -- et peut-être plus -- dans l'implémentation de
std::vector.) Et on évite des variables libres de type pointeur,
quand ce n'est pas nécessaire.

Maintenant, si j'avais à écrire la même chose en C, et à la
place de 10, je n'avais qu'une variable N, j'écrirais bien :

A* p = malloc( sizeof(A)*N ) ;
A* q = malloc( sizeof(A)*N ) ;
p[ 1 ] = q[ 1 ] ;

Dans l'absense de std::vector, je suis bien obligé de faire une
partie de ce qu'il fait manuellement. Mais ce n'est pas fini --
si A a un constructeur, il n'est pas appelé ci-dessus. Et je ne
me suis pas occupé de libérer la mémoire non plus. (Et
l'utilisation d'un glaneur de cellules est bien plus rare en C
qu'en C++.)

contient au moins un membre mutable, on peut le modifier


Par curiosité : quel est l'interret de "mutable" ?


Tant qu'on ne connaît pas le modèle objet de C++ très bien,
rien.

Sérieusement, c'est pour implémenter des caches. L'exemple type,
c'est un segment, qui consiste en deux points :

class Segment
{
// ...
private:
Point2D start ;
Point2D end ;
} ;

Maintenant, supposons que la valeur des Segment change peu
souvent, qu'on a souvent besoin de sa magnitude (mais seulement
pour certains segments, non pour tout), et que la calcule de la
racine carrée sur notre machine est extrèmement cher. Alors, on
pourrait imaginer quelque chose du genre :

class Segment
{
public:
// ...
double magnitude() const ;

private:
Point2D myStart ;
Point2D myEnd ;
mutable Fallible< double > myMagnitude ;
} ;

(Fallible est une classe qui combine une valeur et un bool, qui
asserte sa validité.) La fonction magnitude serait :

double
Segment::magnitude() const
{
if ( ! myMagnitude.isValid() ) {
double deltaX = myEnd.x - myStart.x ;
double deltaY = myEnd.y - myStart.y ;
myMagnitude.validate( sqrt( deltaX*deltaX + deltaY*deltaY )
) ;
}
return myMagnitude.value() ;
}

Puisque magnitude est une fonction const, elle ne doit pas
modifier l'objet. Mais modifier l'élément myMagnitude est
permis, parce que cet élément ne participe pas à la valeur de
l'objet. (Évidemment, dans le Segment ci-dessus, toute opération
qui modifierait la valeur ferait aussi :
myMagnitude.invalidate() ;
.)

--
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
meow
volatile :
il n'y a aucun cas où tu risques de l'utiliser ou de le voir (sauf peut -être dans
un handleur de signal, et encore).
Je n'ai encore jamais touché à la programmation avec des thread, mais

je me demaneds dans quelle mesure volatile ne pourrait pas trouver son
emploie aussi dans ce cadre là si on a deux threads qui jouenty avec
une meme variable... Non ?

A *p = new std::vector< A >() ;
''O_o J'ai vraiment ecrit ça ! Oups, la métabourde :D


J'ai ecrit un peu vite mon exemple, ce que je voulais c'était un
std::vector<A> *p = new std::vector< A >(2) ; //idem pour q
et je suis d'accord sur le fait que ça n'a que peu d'interret... Si ce
n'est d'avoir pris conscience qu'en C++ le pointeur est utilisé
essentielement pour la matérialisation des relations.

mutable :
OK, merci pour les réponses et pour l'exemple, c'est en effet beaucoup
plus clair.
1. ça permet de modifier un attribut non const dans un objet qui lui
est const
2. ça trouve son utilisation dans des cas où un attribut de l'objet
est suffisemment décorellé de l'objet, comme dans le cas d'un cache.


1 2 3