Signification de «const» en dernier dans une déclaration de fonction d'une classe?


727

Quelle est la signification de constdans de telles déclarations? Le constme confond.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

Réponses:


951

Lorsque vous ajoutez le constmot - clé à une méthode, le thispointeur devient essentiellement un pointeur vers un constobjet et vous ne pouvez donc pas modifier les données de membre. (Sauf si vous utilisez mutable, plus à ce sujet plus tard).

Le constmot-clé fait partie de la signature des fonctions, ce qui signifie que vous pouvez implémenter deux méthodes similaires, une qui est appelée lorsque l'objet l'est constet une qui ne l'est pas.

#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 constversion. Si vous changez la déclaration de méthode dans l'exemple ci-dessus par 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 mutableet une constméthode peut ensuite le modifier. Il est principalement utilisé pour les compteurs internes et autres. 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++;    // This works because counter is `mutable`
        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 << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
}

qui produirait

Foo
Foo const
Foo has been invoked 2 times


47

Le constqualificatif signifie que les méthodes peuvent être appelées sur n'importe quelle valeur de foobar. La différence survient lorsque vous envisagez d'appeler une méthode non const sur un objet const. Vérifiez si votre foobartype a la déclaration de méthode supplémentaire suivante:

class foobar {
  ...
  const char* bar();
}

La méthode bar()est non const et n'est accessible qu'à 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 constest cependant de marquer des méthodes qui ne modifieront pas l'état interne de la classe. Il s'agit d'un concept puissant mais qui n'est pas réellement applicable en C ++. C'est plus une promesse qu'une garantie. Et celui qui est souvent cassé et facilement cassé.

foobar& fbNonConst = const_cast<foobar&>(fb1);

3
Je pensais que la réponse concernait les autres méthodes const et non les objets const.
Mykola Golubyev, le

Merci pour "L'idée derrière constest de marquer des méthodes qui ne modifieront pas l'état interne de la classe". C'est vraiment ce que je cherchais.
kovac

1
@JaredPar cela signifie-t-il que toute fonction membre représentant une opération en lecture seule doit être marquée comme const?
kovac

26

Ces const signifient que le compilateur générera une erreur si la méthode «avec 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
}

Lisez ceci pour plus d'informations


1
Une question sur constles fonctions membres qui ne mentionne pas mutable est au mieux incomplète.
IIspectable

13

La réponse de Blair est dans le mille.

Notez toutefois qu'il existe un mutablequalificatif qui peut être ajouté aux membres de données d'une classe. Tout membre ainsi marqué peut être modifié dans une constméthode sans violer le constcontrat.

Vous pouvez utiliser ceci (par exemple) si vous voulez qu'un objet se souvienne combien de fois une méthode particulière est appelée, sans affecter la constance "logique" de cette méthode.


10

Signification d'une fonction membre Const en C ++ Connaissance courante: la 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. Autrement dit, c'est un pointeur constant vers un X non constant (voir Const Pointers et Pointers to Const [7, 21]). Étant donné que l'objet auquel cela se réfère n'est pas const, il peut être modifié. Le type de ceci dans une fonction membre const d'une classe X est const X * const. C'est-à-dire que c'est un pointeur constant vers un X constant. Parce que l'objet auquel cela se réfère 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;
};

thisne l'est pas const. La raison pour laquelle il ne peut pas être modifié est qu'il s'agit d'une valeur.
Brian

7

lorsque vous utilisez constdans la signature de méthode (comme votre dit:) vous dites const char* foo() const;au compilateur que la mémoire pointée par thisne peut pas être modifiée par cette méthode (qui est fooici).


6

Je voudrais ajouter le point suivant.

Vous pouvez également en faire un const &etconst &&

Donc,

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


1
*thisest toujours une valeur l, même si la fonction membre est qualifiée rvalue-ref et est appelée sur une valeur r. Exemple .
HolyBlackCat

1
Oui, alors comment améliorer ma réponse actuelle?
coder3101

Je veux dire quoi écrire en commentaire dans le bloc, qui justifie le comportement
coder3101

Mise à jour. Est-ce OK?
coder3101

2

Le mot clé const utilisé avec la déclaration de fonction spécifie qu'il s'agit d'une fonction membre const et qu'il ne pourra pas modifier les membres de données de l'objet.


1

https://isocpp.org/wiki/faq/const-correctness#const-member-fns

Qu'est-ce qu'une " constfonction membre"?

Une fonction membre qui inspecte (plutôt que mute) son objet.

Une constfonction membre est indiquée par un constsuffixe juste après la liste des paramètres de la fonction membre. Les fonctions membres avec un constsuffixe sont appelées «fonctions membres const» ou «inspecteurs». Les fonctions membres sans constsuffixe sont appelées «fonctions membres non const» ou «mutateurs».

class Fred {
public:
  void inspect() const;   // This member promises NOT to change *this
  void mutate();          // This member function might change *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
  changeable.inspect();   // Okay: doesn't change a changeable object
  changeable.mutate();    // Okay: changes a changeable object
  unchangeable.inspect(); // Okay: doesn't change an unchangeable object
  unchangeable.mutate();  // ERROR: attempt to change unchangeable object
}

La tentative d'appel unchangeable.mutate()est une erreur interceptée lors de la compilation. Il n'y a pas d'espace d'exécution ni de pénalité de vitesse pour const, et vous n'avez pas besoin d'écrire des cas de test pour le vérifier lors de l'exécution.

La fonction de fin constde inspect()membre doit être utilisée pour signifier que la méthode ne changera pas l'état abstrait (visible par le client) de l'objet. C'est légèrement différent de dire que la méthode ne changera pas les «bits bruts» de la structure de l'objet. Les compilateurs C ++ ne sont pas autorisés à prendre l'interprétation «au niveau du bit» à moins qu'ils ne puissent résoudre le problème d'alias, qui ne peut normalement pas être résolu (c'est-à-dire qu'un alias non-const pourrait exister qui pourrait modifier l'état de l'objet). Un autre aperçu (important) de ce problème d'alias: pointer sur un objet avec un pointeur vers const ne garantit pas que l'objet ne changera pas; il promet simplement que l'objet ne changera pas via ce pointeur .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.