To helios: no offense, but what kind of advice is "stay away from these mostly useless and utterly confusing constructs" on a C++ forum? IMHO it is very important to learn how to write const-correct code. The Wiki page is useful. When I was learning how to "parse" declarations with consts all over the place, I took some notes, which I've attached here:
This file explains how const works.
The following declarations are identical:
const char* p;
char const* p;
Both declare a pointer to a constant character. The second is slightly
better in the sense that the declaration can be read from right-to-left:
"p is a pointer to a const char". Read as such, it is easy to see that
the line *p = 'c'; will not compile.
The following declaration:
char* const p;
declares p to be a constant pointer to a character. That is:
p = "foo"; // Does not compile
*p = 'f'; // Compiles!
And thus:
const char* const p;
char const* const p;
both declare p to be a constant pointer to a constant character, and
so none of the following lines of code compile:
p = "foo";
*p = 'f';
Now throw another pointer into the mix:
const char** p;
char const** p;
These are equivalent and declare p to be a pointer to a pointer to a
constant character. That is:
p = ptr-to-ptr-to-char; // Compiles
*p = ptr-to-char; // Compiles
**p = 'f'; // Does not compile
Or how about creative placement of const:
char* const* p;
This declares p to be a pointer to a constant pointer to a character.
That is:
p = ptr-to-constptr-to-char; // Compiles
*p = ptr-to-char; // Does not compile
*p = constptr-to-char; // Does not compile
**p = 'f'; // Compiles
And the ever-popular:
char** const p;
Which declares p to be a constant pointer to a pointer to a character.
Or:
p = ptr-to-ptr-to-char; // Does not compile
p = constptr-to-ptr-to-char; // Does not compile
*p = ptr-to-char; // Compiles
**p = 'f'; // Compiles
And now we get just plain const happy:
const char* const* p;
p is a pointer to a constant pointer to a constant character. The only
thing you can do with this one (besides remove the code and rewrite) is:
p = ptr-to-constptr-to-constchar;
const char** const p;
p is a constant pointer to a pointer to a constant character. The only
thing you can do with this is:
*p = ptr-to-constchar;
And this beast:
const char* const* const p;
Well, it won't pass code review since nobody will understand it, but at
any rate... We've achieved maximum constant-ness with this line. You
can't do anything at all with p, what it points to, what that points to,
or what "what that" points to. You can print it. That's about it.
Ho-ho, and the fun is just beginning. Now throw in REFERENCES!
const char& p;
char const& p;
These both declare p to be a reference to a constant character. That is,
p cannot change.
char& const p;
const char& const p;
char const& const p;
char*& const p;
const char*& const p;
const char* const& const p;
These all generate compiler errors, because there is no such thing as
a constant reference I guess.
const char*& p;
char const*& p;
p is a reference to a pointer to a constant character. One can change p,
but not *p.
char* const& p;
p is a reference to a constant pointer to a character.
const char* const& p;
p is a reference to a constant pointer to a constant character.
const char&* p;
char const&* p;
char& const* p;
char&* const p;
const char& const* p;
const char&* const p;
const char& const* const p;
Fortunately pointers to references are not allowed. The above declarations
are illegal. The programmer who attempts to use the above declarations
should be fired anyway.
As for mutable and const member functions.
Here's an example:
1 2 3 4 5 6 7 8
|
class Foo {
public:
void DoSomething() const;
private:
int someData;
char moreData;
float yetMoreData;
};
|
Note the "const" after DoSomething(). This means that DoSomething() is a const member function. This says that DoSomething() will not modify any of its own data members when it executes. Why is saying this useful? Because it is a contract to Foo's user. Let's extend the example:
1 2 3 4 5 6 7 8 9 10 11 12
|
class Foo {
public:
void DoSomething() const;
void WorkHard();
void PrintMe() const
{ cout << someData << ", " << moreData << ", " << yetMoreData << endl; }
private:
int someData;
char moreData;
float yetMoreData;
};
|
(I've omitted things like constructors, etc, to distill the code to its bare essence)
Suppose you have a variable of type Foo, and that Foo's data members were
actually initialized to something useful:
1 2 3 4 5
|
Foo f; // Let's say someData = 42, moreData = 'X', and yetMoreData = 3.14.
f.PrintMe(); // Prints 42, X, 3.14
f.DoSomething();
f.PrintMe(); // Better print 42, X, and 3.14
|
Why do I say that the second call to PrintMe() better print the same values as
the first call? How can I even know as a user of Foo, since I can't see what
DoSomething() does? Because DoSomething() is a const member function. It is
a contract to me, the user, that says that DoSomething() will not modify any of its
(non-mutable, but more on that later) data members.
Now notice that WorkHard() is not declared as a const member function. So:
1 2 3 4 5
|
Foo f; // Let's say someData = 42, moreData = 'X', and yetMoreData = 3.14.
f.PrintMe(); // Prints 42, X, 3.14
f.WorkHard();
f.PrintMe(); // What will this print?
|
Because WorkHard() is not declared const, it means that WorkHard() might modify one or more of the data members. Hopefully the description of the function tells me exactly what it does. For example "multiplies someData by 2, increments moreData and wraps
at 'Z' back to 'A'. Does not modify yetMoreData." Unfortuantely, in practice, too many developers don't document well and don't follow const-correctness, and all too often it actually becomes hard to know whether a member function will modify state or not without looking at the implementation of the function (sometimes it is not obvious).
The point is... declaring a member function const is part of the API that you are giving to the programmer who will use your code.
Now, on to mutable. First, a classroom example, and then I'll give you a real-world example of where I used mutable in my code (I cannot publish the code here, but I will explain why I needed/wanted to use mutable).
So const is part of the API of the class to the programmer, to guarantee that the object won't "change state" across calls to the function. Sometimes, your object might have some very internal data that your user couldn't possibly care about. For example, let's say you have a class that can send messages on a socket.
1 2 3 4 5 6 7
|
class MySocket {
public:
// ...
int Send( const char* data, int length ) const;
private:
int sock; // The file descriptor used to send data
};
|
I declared Send() to be const, because it doesn't change any data members (sock).
Now, suppose for whatever reason I, the programmer of Socket, want to copy the
user's data into an internal cache in the class. Let's also say that my user cannot possibly care about what is in the internal cache.
1 2 3 4 5 6 7 8
|
class MySocket {
public:
// ...
int Send( const char* data, int length ) const;
private:
int sock; // The file descriptor used to send data
mutable char cache[ 4096 ]; // Ok, yes, hard-coded constant is hideous. anyway.
};
|
So now Send() wants to copy the user's data into the cache, but it can't because
Send() is a const member function. So I make my cache mutable, and now Send()
can do this.
Why, you ask, don't I just make Send() non-const and forget the mutable? Possibly two reasons. First, if there is a lot of code using Socket already, it might be impractical for me to change Send() this way without causing a lot of compile errors (because my users might have been relying on Send() being const when they wrote their const-correct code). Second, as I stipulated earlier, my users can't possibly care that the cache changed when Send() was called.