Par exemple, si vous vouliez créer une fonction f() qui reçoit une chaîne de caractères, si de plus vous voulez promettre à la fonction appelante de ne pas changer l'argument de type chaîne de caractères qu'elle passe à f(), vous pouvez déclarer l'argument string de f() comme ...
Lors du passage par const-réference et du passage par const-pointeur, toutes tentatives de changer l'argument de type string à l'intérieur de f() se traduirait par une erreur à la compilation. Ce contrôle est fait entièrement au moment de la compilation: il n'y a là aucun coût en terme de mémoire ou vitesse d'excution en contre partie de l'utilisation de const. Dans le cas du passage par valeur f3(), la fonction appelée obtient une copie de la chaîne de caractère en argument. Ceci signifie que f3() peut changer sa copie locale, mais la copie est détruite quand f3() retourne. Par conséquent f3() ne peut pas changer l'objet chaîne de caractères.
Si au contraire vous vouliez créer une fonction g() qui reçoit une chaîne de caractères, et que vous vouliez faire savoir que l' argument de g() peut être modifié (peut muter). Dans ce cas-ci vous pourriez écrire g() ainsi ...
L'absence de const dans ces fonctions dit au compilateur que la fonction peut (mais ça n'est pas une exigence) de changer la chaîne de caractères en argument. Ainsi les fonctions appelantes peuvent passer une chaîne de caractères à n'importe quelle f() fonctions, mais seulement f3() (celui qui reoit son paramêtre "par valeur") peut passer la chaîne de caractêres à g1() ou g2(). Si f1() ou f2() ont besoin d'appeler l'une ou l'autre des fonctions g(), une copie locale de la chaîne de caractères doit être passée vers g(); le paramtêre de f1() ou f2() ne peut pas être directement passé vers g(). Par exemple
void g1(string& s);
void f1(const string& s)
{
g1(s); // erreur au moment de la compilation car s est const
string localCopy = s;
g1(localCopy); // CORRECT puisque localCopy n'est pas const
}
Naturellement dans le cas ci-dessus, tout changement à l'intérieur de g1() est en fait appliqué à l'objet localCopy de f1(). En particulier, il n'y a pas de modification du paramêtre passé par réference const à f1().
[ Haut | Bas | Section précédente | Section suivante ]
Si vous trouvez que les aides ordinaires de sûreté de type vous aide à obtenir des systmes corrects (elles le font; particulirement dans de grands systèmes), vous trouverez que l'utilisation correcte de const vous aide galement.
[ Haut | Bas | Section précédente | Section suivante ]
Utiliser const correctment après coup cause un effet de boule neige: chaque const ajouté "par ici" exige quatre const de plus "par là."
[ Haut | Bas | Section précédente | Section suivante ]
Par exemple, si la classe Fred a une fonction membre const appelée inspect(), p->inspect() est CORRECT. Mais si la classe Fred a une fonction membre non-const appelée mutate(), p->mutate() est une erreur (l'erreur est décelée par le compilateur; aucun essai d'exécution n'est fait, ce qui signifie que const ne ralentit pas votre programme).
[ Haut | Bas | Section précédente | Section suivante ]
[ Haut | Bas | Section précédente | Section suivante ]
Par exemple, si la classe Fred a une fonction membre const appelée inspect(), x.inspect() est CORRECT. Mais si la classe Fred a une fonction membre non-const appelée mutate(), x.mutate() est une erreur (l'erreur est décelée par le compilateur; aucun essai d'exécution n'est fait, ce qui signifie que const ne ralentit pas votre programme).
[ Haut | Bas | Section précédente | Section suivante ]
Pour avoir une idée de ce que signifie cette déclaration, vous devez lire de droite à gauche. Donc "Fred& const x" signifie que "x est une const réference à un Fred". Mais c'est redondant puisque que les réferences sont toujours const: vous ne pouvez pas changer le réferant d'une réference. Jamais. Avec ou sans const.
En d'autres termes, "Fred& const x" est fonctionellement équivalent à "Fred& x". Puisque vous n'obtenez rien en ajoutant le const après le &, vous ne devriez pas l'utiliser pour éviter de créer des problèmes. I.e. le const peut amener certaines personnes à penser que l'objet de type Fred est const, comme si vous aviez "const Fred& x".
[ Haut | Bas | Section précédente | Section suivante ]
Le problème avec l'utilisation de "Fred const& x" (avec le const avant) est qu'il pourrait facilement être mal tappé en une erreur "Fred &const x" (avec le const aprês).
Amliorez pour utiliser simplement le const Fred& X.
[ Haut | Bas | Section précédente | Section suivante ]
Une fonction membre qui inspecte (par opposition a modifie) l'objet implicite.
Une fonction membre const est reperable grâce au const suffixe juste apres la liste des paramètres. Les fonctions membres avec le suffixe const sont appelées "fonctions membre const" ou "inspecteurs". Les fonctions membres sans un suffixe const sont appelées "fonctions membres non-const" ou "mutators".
class Fred {
public:
void inspect() const; // Ce membre promet de NE PAS modifier *this
void mutate(); // Ce membre peut eventuellement modifier *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
changeable.inspect(); // OK: ne modifie pas l'objet
changeable.mutate(); // OK: modifie un objet modifiable
unchangeable.inspect(); // OK: ne modifie pas un objet non-modifiable
unchangeable.mutate(); // ERROR: tentative de modifier un objet non-modifiable
}
L'erreur dans unchageable.mutate() est saisie à la compilation. Le programme n'est pas pénalisées à l'execution que ce soit en utilisation mémoire ou en vitesse.
Le const apres la fonction membre inspect() signifie que l'état abstrait (visible par le client) ne changera pas. C'est légerement différent par rapport à promettre que la respresentation en bits de l'objet ne changera pas. Les compilateurs C++ ne peuvent pas choisir l'interpretation "au bit près" à condition qu'ils soient capables de résoudre le problème d'aliassage, qui normalement ne peut pas être résolu (ie. un alias non-const pourrait exister lequel modifierait l'état de l'objet). Un autre point de vue (important) sur ce problème d'aliassage: pointer sur un objet avec un pointeur sur const ne garantie pas que l'objet ne changera pas; cela promet au plus que l'objet ne changera pas via ce pointeur.
[ Haut | Bas | Section précédente | Section suivante ]
Utilisez mutable, ou utiliser const_cast.
Un faible pourcentage des inspecteurs doivent faire des changements inoffensifs à des donnés membre (e.g. un objet List peut vouloir cacher la dernière recherche dans l'espoir d'amméliorer la performance de la recherche suivante). En disant "inoffensif", je veux dire que les changements ne sont pas visible depuis l'interface de l'objet (dans le cas contraire, la fonction membre ne serait pas un inspecteur).
Quand cela arrive, la donné membre qui va être modifiée doit être marquée mutable (mettez le mot clé mutable avant la déclaration de la donnée membre. Si votre compilateur ne supporte pas le mot clé mutable, vous pouvez transtyper this avec le mot clé const_cast. Eg. dans List::lookup() const, vous pourriez faire,
List * self = const_cast<List *>(this);
Après cette ligne, self aura les mème bits que this (e.g. self == this), mais self est un List * au lieu d'un const List * avant. Par conséquent, vous pouvez utiliser self pour modifier l'objet pointé par this.
[ Haut | Bas | Section précédente | Section suivante ]
En théorie oui; en pratique non.
Même si le langage déconseille const_cast, le seul moyen d'éviter de vider le cache registre lors d'un appel à une fonction membre const serait de résoudre le problème d'aliassage (ie. de prouver qu'il n'y a pas de pointeur non-const sur l'objet). Ceci arrive seulement en de rares occasions (lorsque l'objet est construit dans la portée de l'invocation de la fonction membre const, et lorsque toutes les invocations de membres non-const entre la construction de l'objet et l'invocation de la fonction membre const ne sont pas virtuelles, et lorsque chacune de ces invocations est aussi inline, et lorsque le constructeur lui-mêmeme est déclaré inline, et lorsque toutes les fonctions membres appelées par le constructeur sont aussi inline).
[ Haut | Bas | Section précédente | Section suivante ]
Parce que "const int * p" signifie "p promet de ne pas changer *p", et pas "*p promet de ne pas changer".
Faire pointer un const int * sur un int ne const-ifie pas le int pointé. Le int ne peut plus être changé par le const int *, mais si quelqu'un d'autre a un int * (note: non const) qui pointe ("aliasse") the même int, alors le int * peut être utilisé pour changer le int. Par exemple:
void f(const int* p1, int* p2)
{
int i = *p1; // Obtient la valeur (originale) de *p1
*p2 = 7; // Si p1 == p2, ceci changera aussi *p1
int j = *p1; // Obtient lai valeur (eventuellement nouvelle) de *p1
if (i != j) {
cout << "*p1 a change, mais il n'a pas change via le pointeur p1!\n";
assert(p1 == p2); // C'est le seul moyen par lequel *p1 pourrait etre different
}
}
main()
{
int x;
f(&x, &x); // Cela est parfaitement legal (et meme moral!)
}
Notez que main() et f(const int *, int *) pourraient être dans différentes unités de compilation compilées différents jours de la semaine. Dans ce case, il n'y a pas de moyen pour le compilateur de détecter l'aliassage à la compilation. Par conséquent, il n'y a pas de moyen de créer une règle qui interdit ce genre de chose. En fait, nous ne voudrions même pas créer une telle règle, puisque en général on consid&egrav;re que c'est une qualité de pouvoir avoir plusieur pointeurs pointant sur la même chose. Le fait que l'un de ces pointeur promette de ne pas changer la "chose" sous-jacente est une promesse faite par le pointeur; et non une promesse faite par la "chose".
[ Haut | Bas | Section précédente | Section suivante ]
No! (c'est lié à la FAQ sur l'aliassage des pointeur int)
"const Fred * p" signifie que Fred ne peut pas être modifié via le pointeur p, mais n'importe quel pointeur non const qui aliasse peut être utilisé pour changer l'objet Fred. Par exemple, si vous avez deux pointeurs "const Fred * p" et "Fred * q" qui pointent sur le même objet Fred (aliassé), le pointeur q peut être utilisé pour changer l'objet Fred mais le pointeur p ne peut pas,
class Fred {
public:
void inspect() const; // A fonciton membre const
void mutate(); // A fonction membre non-const
};
main()
{
Fred f;
const Fred* p = &f;
Fred* q = &f;
p->inspect(); // OK: pas de changement via p
p->mutate(); // Erreur: ne peut pas changer *p via p
q->inspect(); // OK: q est autorise a inspecter l'objet
q->mutate(); // OK: q est autorise a modifier l'objet
}
[ Haut | Bas | Section précédente | Section suivante ]
Ecrire à l'auteur,
au traducteur,
ou en savoir plus sur la traduction.
[ C++ FAQ Lite fr |
Table des matières |
Index |
A propos de l'auteur |
© |
Téléchargez votre propre copie ]
Dernière révision le 12 Nov 2002