2009-01-18 6 views
110

En tant que programmeur C++ débutant, il y a quelques constructions qui me semblent encore très obscures, l'une d'entre elles est const. Vous pouvez l'utiliser dans de nombreux endroits et avec autant d'effets différents qu'il est presque impossible pour un débutant de sortir vivant. Est-ce qu'un gourou C++ expliquera une fois pour toutes les différentes utilisations et si et/ou pourquoi ne pas les utiliser?Combien et quelles sont les utilisations de "const" en C++?

+0

exactement à la recherche de cette question: D – alamin

Répondre

94

Essayer de recueillir des utilisations:

Binding certains temporaires pour faire référence à const, pour allonger sa durée de vie. La référence peut être une base - et la destructor de celui-ci n'a pas besoin d'être virtuel - le droit destructor est encore appelé:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction); 

Explication, en utilisant le code:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual 
}; 

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { } 
    ~Derived() { 
     t(); // call function 
    } 
}; 

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); } 

Cette trick est utilisé dans la classe d'utilité ScopeGuard d'Alexandrescu. Une fois que le temporaire est hors de portée, le destructeur de Derived est appelé correctement. Le code ci-dessus manque quelques petits détails, mais c'est le gros problème.


Utilisez const pour dire aux autres méthodes ne changera pas l'état logique de cet objet.

struct SmartPtr { 
    int getCopies() const { return mCopiesMade; } 
}; 

Utilisez const pour la copie-sur-écriture des classes, pour faire l'aide du compilateur vous de décider quand et quand ne vous devez copier.

struct MyString { 
    char * getData() { /* copy: caller might write */ return mData; } 
    char const* getData() const { return mData; } 
}; 

Explication: Vous pouvez partager des données lorsque vous copiez quelque chose d'aussi longtemps que les données de l'origine et l'objet copie'd restent les mêmes. Une fois que l'un des objets change de données, vous avez besoin de deux versions: une pour l'original et une pour la copie. Autrement dit, vous copier sur un écrire à l'un ou l'autre objet, de sorte qu'ils ont maintenant tous les deux leur propre version.

En utilisant le code:

int main() { 
    string const a = "1234"; 
    string const b = a; 
    // outputs the same address for COW strings 
    cout << (void*)&a[0] << ", " << (void*)&b[0]; 
} 

L'imprimante extrait ci-dessus la même adresse sur mon GCC, car la bibliothèque C++ utilisé implémente une copie sur écriture std::string. Les deux chaînes, même si elles sont des objets distincts, partagent la même mémoire pour leurs données de chaîne. Faire b non-const préférera la version non-const du operator[] et GCC va créer une copie de la mémoire tampon de sauvegarde, car nous pourrions le changer et il ne doit pas affecter les données de a!

int main() { 
    string const a = "1234"; 
    string b = a; 
    // outputs different addresses! 
    cout << (void*)&a[0] << ", " << (void*)&b[0]; 
} 

Pour la copie-constructeur de faire des copies à partir d'objets const et Temporaries:

struct MyClass { 
    MyClass(MyClass const& that) { /* make copy of that */ } 
}; 

Pour faire des constantes qui trivialement ne peut pas changer

double const PI = 3.1415; 

Pour passer des objets arbitraires par référence au lieu de la valeur par - pour éviter peut-être coûteux, voire impossible, par valeur passant

void PrintIt(Object const& obj) { 
    // ... 
} 
+2

Pouvez-vous expliquer s'il vous plaît la première et la troisième utilisation dans vos exemples? – tunnuz

+0

"Pour garantir à l'appelé que le paramètre ne peut pas être NULL", je ne vois pas comment const a quelque chose à voir avec cet exemple. –

+0

oups, j'échoue tellement. J'ai commencé à écrire des références. je vous remercie beaucoup pour gémir :) je vais bien sûr supprimer ce genre de choses maintenant :) –

23

Il y a vraiment deux utilisations principales de const en C++.

Const valeurs

Si une valeur est sous la forme d'une variable, membre ou d'un paramètre qui ne (ou ne devrait pas) être modifiée au cours de sa durée de vie, vous devriez marquer const. Cela aide à prévenir les mutations sur l'objet. Par exemple, dans la fonction suivante, je n'ai pas besoin de changer l'instance de Student passée donc je la marque const.

void PrintStudent(const Student& student) { 
    cout << student.GetName(); 
} 

En ce qui concerne pourquoi vous le feriez. Il est beaucoup plus facile de raisonner sur un algorithme si vous savez que les données sous-jacentes ne peuvent pas changer. "const" aide, mais ne garantit pas que cela sera réalisé.

De toute évidence, les données d'impression à Cout ne nécessite pas beaucoup de pensée :)

Marquage d'une méthode membre const

Dans l'exemple précédent, je marqué des étudiants const. Mais comment C++ savait-il que l'appel de la méthode GetName() à l'étudiant ne ferait pas muter l'objet? La réponse est que la méthode a été marquée comme const. Marquer une méthode "const" fait 2 choses. Principalement il indique à C++ que cette méthode ne mutera pas mon objet. La deuxième chose est que toutes les variables membres seront maintenant traitées comme si elles étaient marquées comme const. Cela vous aide mais ne vous empêche pas de modifier l'instance de votre classe.

Ceci est un exemple extrêmement simple mais j'espère que cela aidera à répondre à vos questions.

13

Prenez soin de comprendre la différence entre ces 4 déclarations:

Les 2 déclarations suivantes sont identiques sémantiquement. Vous pouvez changer où point ccp1 et ccp2, mais vous ne pouvez pas changer la chose qu'ils pointent. Ensuite, le pointeur est const, donc pour être significatif, il doit être initialisé pour pointer vers quelque chose.

const char* ccp1; 
char const* ccp2; 
Vous ne pouvez pas le faire pointer vers quelque chose d'autre, mais la chose qu'il indique peut être changé.

char* const cpc = &something_possibly_not_const; 

Enfin, nous combinons les deux - si la chose étant pointé du doigt ne peut pas être modifié, et le pointeur ne peut pas pointer vers nulle part ailleurs.

const char* const ccpc = &const_obj; 

La règle en spirale dans le sens horaire peut aider à dénouer une déclaration http://c-faq.com/decl/spiral.anderson.html

+0

D'une manière détournée, oui c'est le cas! La règle en spirale dans le sens des aiguilles d'une montre le décrit mieux: commencez par le nom (kpPointer) et tracez une spirale dans le sens des aiguilles d'une montre à travers le jeton, et dites chaque jeton. Evidemment, il n'y a rien à droite de kpPointer mais ça marche toujours. –

2

Comme une petite note, comme je l'ai lu here, il est utile de noter que

const applique à tout ce qui est sur son immédiate à gauche (sauf si il n'y a rien, auquel cas il s'applique à tout ce qui est immédiatement à droite).

Questions connexes