views:

121

answers:

4

I am reading a book called 'Effective C++, Second Edition' and its talking about const member functions and how you have bitwise const-ness and conceptual const-ness.

It says most compilers will go with bitwise const-ness, which is that you cannot alter data members of an object inside a const member function.

Then there's an example of a member function that doesn't seem to act bitwise in the const test.

It goes like this:

#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;
}

When this is run, it says in my book it should allow you to change the value of s, even though its const. This because char* data is pointing to the same as const char* value is pointing. *data in this case is not const.

However trying to run this in MS VC++ 6.0, it throws an access violation at line *nasty = 'M';

Can someone explain what is going on? I think I've missed something?

To me it seems that because we have a const mystring s, we should not be able to change it, but then what it says in the books seems awkward.

+7  A: 

The access violation is because you try to change a string literal. Your code is equivalent to:

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

which is illegal in both C and C++ - nothing to do with const member functions.

anon
so I guess string literal is an immutable object?
Tony
@Tony: it is undefined whether in any given implementation a string literal is *really* immutable object from the point of view of the standard. The standard only tells you that object that it is undefined behavior to modify objects that **are** constant (as compared to non-constant objects accessed through a constant reference --see my answer)
David Rodríguez - dribeas
@Tony I don't know about that - all the C and C++ standards say is that attempting to modify one leads to undefined behaviour.
anon
Generally copy-in semantics are advisable. And in this case a copy-in of the string in your constructor might be the best way to prevent this type of problem. On the other hand, changing the data member to be const and the cast operator to return a const char* would work too. Either would make things consistent. Your constructor should not assume it can cast away the const. That's another lesson to be learned with this example, besides the behavior of const.
Craig W. Wright
+1  A: 

You get access violation only because the char* pointer is to a string literal. Changing a string literal is undefined behavior (AV in your case), it has nothing to do with const-correctness.

sharptooth
so what is the right way to amend the string literal?
Tony
@Tony: use `std::strings`: `std::string salute = "Hi"; salute = "Good bye";`. The C solution would be to copy and modify the copy, use the literal to create a non-const object... `char text[] = "Hi!"; text[0] = 'B'; text[1] = 'y'; text[2] = 'e';` --note that arrays cannot change size, editting `text` to "Bye" works only because "Hi!" and "Bye" take the same amount of memory. Also note that it is defined as `char text[]` and not `char *text`, arrays and pointers **are not** the same.
David Rodríguez - dribeas
A: 

What you are doing is undefined behavior. You are casting away const-ness and trying to modify a constant value. If that example has been copied as-is from the book, then the book is wrong.

What is legal to do with const_cast<> is casting away constness from a constant pointer/reference to a non-const object:

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

The reason that you cannot cast away const-ness from a reference to a real const object is that the compiler can decide to place the object in read-only memory, in which case the operation can fail in different ways (killing the application, ignoring the change...)

David Rodríguez - dribeas
A: 

In your example, you don't change s, you try to change the memory where a member variable of s points to. Since you are allowed to place the const in many places, you have to be careful about what you actually declare const.

Your member function operator char*() const is not allowed to change any member variable. Try operator char *() const { data = "something else"; return data; } and your compiler will tell you that you are not allowed to modify data. However, in this case you simply return data without modification. That's allowed. You are even allowed to change the memory where data points to, as in operator char *() const { *data = 'M'; return data; }. However, this fails in your context since data points to a string literal which you are not allowed to change.

Malte Clasen
So why is this valid: const char* v = "Hello"; v = "Tony"; ?
Tony
@Tony: `v` is a pointer to constant data, initially pointing to a string literal containing `"Hello"`. `v = "Tony"` changes it to point at a different string literal. It doesn't modify the first string.
Mike Seymour
Be careful where to place that `const`: `char * const v = "Hello"; v = "Tony";` is a constant pointer to non-const data, so the assignment results in a compiler error.
Malte Clasen