Quel est le sens de const
dans des déclarations comme celles-ci? La const
me déroute.
class foobar
{
public:
operator int () const;
const char* foo() const;
};
Lorsque vous ajoutez le mot clé const
à une méthode, le pointeur this
devient essentiellement un pointeur sur l'objet const
. Par conséquent, vous ne pouvez modifier aucune donnée de membre. (Sauf si vous utilisez mutable
, vous en saurez plus à ce sujet plus tard).
Le mot clé const
fait partie de la signature de la fonction, ce qui signifie que vous pouvez implémenter deux méthodes similaires, l'une appelée lorsque l'objet est const
et l'autre non.
#include <iostream>
class MyClass
{
private:
int counter;
public:
void Foo()
{
std::cout << "Foo" << std::endl;
}
void Foo() const
{
std::cout << "Foo const" << std::endl;
}
};
int main()
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
}
Cela produira
Foo
Foo const
Dans la méthode non-const, vous pouvez modifier les membres de l'instance, ce que vous ne pouvez pas faire dans la version const
. Si vous modifiez la déclaration de méthode dans l'exemple ci-dessus pour le code ci-dessous, vous obtiendrez des erreurs.
void Foo()
{
counter++; //this works
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++; //this will not compile
std::cout << "Foo const" << std::endl;
}
Ce n'est pas tout à fait vrai, car vous pouvez marquer un membre comme étant mutable
et une méthode const
peut alors le changer. Il est principalement utilisé pour les compteurs internes. La solution pour cela serait le code ci-dessous.
#include <iostream>
class MyClass
{
private:
mutable int counter;
public:
MyClass() : counter(0) {}
void Foo()
{
counter++;
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++;
std::cout << "Foo const" << std::endl;
}
int GetInvocations() const
{
return counter;
}
};
int main(void)
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
std::cout << "The MyClass instance has been invoked " << ccc.GetInvocations() << " times" << endl;
}
qui produirait
Foo
Foo const
The MyClass instance has been invoked 2 times
La constante signifie que la méthode promet de ne modifier aucun membre de la classe. Vous seriez capable d'exécuter les membres de l'objet qui sont ainsi marqués, même si l'objet lui-même était marqué const
:
const foobar fb;
fb.foo();
serait légal.
Voir Combien et quelles sont les utilisations de "const" en C++? pour plus d'informations.
Le qualificateur const
signifie que les méthodes peuvent être appelées sur n'importe quelle valeur de foobar
. La différence vient lorsque vous envisagez d'appeler une méthode non-const sur un objet const. Déterminez si votre type foobar
avait la déclaration de méthode supplémentaire suivante:
class foobar {
...
const char* bar();
}
La méthode bar()
est non-constante et est accessible uniquement à partir de valeurs non-const.
void func1(const foobar& fb1, foobar& fb2) {
const char* v1 = fb1.bar(); // won't compile
const char* v2 = fb2.bar(); // works
}
L'idée derrière const
est cependant de marquer des méthodes qui ne modifieront pas l'état interne de la classe. C'est un concept puissant, mais qui n'est pas réellement applicable en C++. C'est plus une promesse qu'une garantie. Et un qui est souvent cassé et facilement cassé.
foobar& fbNonConst = const_cast<foobar&>(fb1);
Ces const signifient que le compilateur Error si la méthode 'with const' modifie les données internes.
class A
{
public:
A():member_()
{
}
int hashGetter() const
{
state_ = 1;
return member_;
}
int goodGetter() const
{
return member_;
}
int getter() const
{
//member_ = 2; // error
return member_;
}
int badGetter()
{
return member_;
}
private:
mutable int state_;
int member_;
};
Le test
int main()
{
const A a1;
a1.badGetter(); // doesn't work
a1.goodGetter(); // works
a1.hashGetter(); // works
A a2;
a2.badGetter(); // works
a2.goodGetter(); // works
a2.hashGetter(); // works
}
Lire this pour plus d'informations
La réponse de Blair est sur la marque.
Cependant, notez qu'il existe un qualificatif mutable
qui peut être ajouté aux membres de données d'une classe. Tout membre ainsi marqué can peut être modifié dans une méthode const
sans violer le contrat const
.
Vous voudrez peut-être utiliser ceci (par exemple) si vous voulez qu'un objet se souvienne du nombre de fois qu'une méthode particulière est appelée, sans affecter la constness "logique" de cette méthode.
Signification d'une fonction membre Const dans Connaissance commune C++: Programmation intermédiaire essentielle donne une explication claire:
Le type du pointeur this dans une fonction membre non-const d'une classe X est X * const. C’est-à-dire qu’il s’agit d’un pointeur constant sur un X non constant (voir Pointers Const et Pointers to Const [7, 21]). Parce que l'objet auquel il fait référence n'est pas const, il peut être modifié. Le type de this dans une fonction membre const d'une classe X est const X * const. C’est-à-dire qu’il s’agit d’un pointeur constant sur une constante X. Comme l’objet auquel il fait référence est const, il ne peut pas être modifié. C’est la différence entre les fonctions membres const et non const.
Donc dans votre code:
class foobar
{
public:
operator int () const;
const char* foo() const;
};
Vous pouvez le penser comme ceci:
class foobar
{
public:
operator int (const foobar * const this) const;
const char* foo(const foobar * const this) const;
};
lorsque vous utilisez const
dans la signature de la méthode (comme votre dit: const char* foo() const;
), vous indiquez au compilateur que la mémoire pointée par this
ne peut pas être modifiée par cette méthode (foo
ici).
Je voudrais ajouter le point suivant.
Vous pouvez aussi fairec'est un const &
et const &&
Alors,
struct s{
void val1() const {
// *this is const here. Hence this function cannot modify any member of *this
}
void val2() const & {
// *this is const& here
}
void val3() const && {
// The object calling this function should be const rvalue only.
}
void val4() && {
// The object calling this function should be rvalue reference only.
}
};
int main(){
s a;
a.val1(); //okay
a.val2(); //okay
// a.val3() not okay, a is not rvalue will be okay if called like
std::move(a).val3(); // okay, move makes it a rvalue
}
N'hésitez pas à améliorer la réponse. Je ne suis pas un expert
Le mot clé const utilisé avec la déclaration de fonction spécifie qu'il s'agit d'un fonction membre const et qu'il va ne peut pas être modifié les données membres du objet.