2010-07-02 13 views
2

Je suis en train de lire un livre intitulé 'Effective C++, Second Edition' et de parler des fonctions des membres const et de la constance des bits et de la constance conceptuelle.C++ const member function

Il est dit que la plupart des compilateurs iront avec une constance au niveau du bit, c'est-à-dire que vous ne pouvez pas modifier les membres de données d'un objet dans une fonction membre const.

Ensuite, il y a un exemple de fonction membre qui ne semble pas agir au niveau du bit dans le test const.

Il va comme ceci:

#include "stdafx.h" 
#include <string> 
#include <iostream.h> 

using namespace std; 

class mystring 
{ 

public: 
    mystring(const char* value); 

    operator char *() const { return data; } 

private: 
    char * data; 
}; 

mystring::mystring(const char * value) 
{ 

    mystring::data = const_cast<char*>(value); 
} 


int main(int argc, char* argv[]) 
{ 
    const mystring s = "Hello"; 

    char * nasty = s; 

    *nasty = 'M'; 

    printf("s: %c", s); 

    return 0; 
} 

Lorsque cela est exécuté, il dit dans mon livre, il devrait vous permettre de changer la valeur de s, même si son const. Ceci parce que les données char * pointent vers la même valeur que la valeur const char*. *data dans ce cas n'est pas const.

Cependant essayer de tourner ceci dans MS VC++ 6.0, il lance une violation d'accès à la ligne *nasty = 'M';

Quelqu'un peut-il expliquer ce qui se passe? Je pense que j'ai raté quelque chose?

Pour moi, il semble que parce que nous avons un mystring s const, nous ne devrions pas être en mesure de le changer, mais alors ce qu'il dit dans les livres semble maladroit.

+2

Ceci n'est pas pertinent à la question, mais gardez à l'esprit que cette édition du livre, et Visual Studio 6, datent des jours avant que la langue ait été standardisée, quand beaucoup d'idiomes C++ communs n'avaient pas été pensé pour le moment. Si vous voulez apprendre comment C++ est utilisé aujourd'hui, vous seriez mieux avec la troisième édition (que je recommande vivement), et un compilateur moderne. Visual Studio Express est gratuit à télécharger, si vous voulez rester avec Microsoft. –

Répondre

7

La violation d'accès est due au fait que vous essayez de modifier un littéral de chaîne. Votre code est équivalent à:

char * p = "Hello"; 
* p = 'M'; 

ce qui est illégal dans les deux C et C++ - rien à voir avec les fonctions membres const.

+0

donc je suppose que la chaîne littérale est un objet immuable? –

+0

@Tony: il n'est pas défini si, dans une implémentation donnée, une chaîne littérale est * vraiment * un objet immuable du point de vue de la norme. La norme vous indique uniquement que l'objet est un comportement indéfini pour modifier les objets qui sont ** constants (par rapport aux objets non constants accessibles via une référence constante - voir ma réponse) –

+0

@Tony Je ne sais pas que - toutes les normes C et C++ disent que tenter de modifier un conduit à un comportement indéfini. –

2

Vous obtenez une violation d'accès uniquement parce que le pointeur char* correspond à un littéral de chaîne. Changer un littéral de chaîne est un comportement non défini (AV dans votre cas), cela n'a rien à voir avec const-correct.

+0

alors quelle est la bonne façon de modifier la chaîne littérale? –

+0

@Tony: utilise 'std :: strings':' std :: string salute = "Salut"; salut = "Au revoir"; '. La solution C serait de copier et de modifier la copie, d'utiliser le littéral pour créer un objet non-const ... 'char text [] =" Hi! "; text [0] = 'B'; text [1] = 'y'; text [2] = 'e'; '--note que les tableaux ne peuvent pas changer de taille, éditer' text' à "Bye" ne fonctionne que parce que "Hi!" et "Bye" prend la même quantité de mémoire. Notez également qu'il est défini comme 'char text []' et non pas 'char * text', les tableaux et les pointeurs ** ne sont pas les mêmes. –

0

Ce que vous faites est un comportement indéfini. Vous rejetez la constance et essayez de modifier une valeur constante. Si cet exemple a été copié tel quel dans le livre, alors le livre est faux.

Ce qui est légal de le faire avec const_cast<> est coulée constness d'un écart constant pointeur/référence à un objet non-const:

int i = 5; 
int const & ir = i; 
const_cast<int&>(ir) = 7; // valid 
int const * ip = &i; 
*const_cast<int*>(ip) = 9; // valid 

const int c = 11; 
int const & cr = c; 
const_cast<int&>(cr) = 13; // Undefined behavior 

La raison pour laquelle vous ne pouvez pas jeter constness d'une distance référence à un L'objet const réel est que le compilateur peut décider de placer l'objet en mémoire morte, auquel cas l'opération peut échouer de différentes manières (tuer l'application, ignorer le changement ...)

0

Dans votre exemple, vous ne changez pas s, vous essayez de changer la mémoire où une variable membre de s points à. Puisque vous êtes autorisé à placer le const dans de nombreux endroits, vous devez faire attention à ce que vous déclarez réellement const.

Votre fonction membre operator char*() const n'est pas autorisé à modifier une variable membre. Essayez operator char *() const { data = "something else"; return data; } et votre compilateur vous dira que vous n'êtes pas autorisé à modifier data. Cependant, dans ce cas vous renvoyez simplement data sans modification. C'est autorisé.Vous êtes même autorisé à changer la mémoire où data points, comme dans operator char *() const { *data = 'M'; return data; }. Cependant, cela échoue dans votre contexte puisque data pointe vers un littéral de chaîne que vous n'êtes pas autorisé à modifier.

+0

Alors pourquoi est-ce valide: \t const char * v = "Bonjour"; \t v = "Tony"; ? –

+1

@Tony: 'v' est un pointeur vers des données constantes, pointant initialement vers un littéral de chaîne contenant' 'Hello ''. 'v =" Tony "' change pour pointer sur un littéral de chaîne différent. Cela ne modifie pas la première chaîne. –

+1

Faites attention où placer 'const':' char * const v = "Bonjour"; v = "Tony"; 'est un pointeur constant vers des données non-const, donc l'affectation entraîne une erreur de compilation. –