A word of warning at the beginning: This post is about C++, not about C and I will not explain the basics of pointer arithmetics! So whatever you read here may not necessarily apply to C. Nevertheless you may have a look Sugih’s page which features are in C.
Introduction
While writing C++11 code I was faced with the task of passing variables to functions which will not alter these. Or pure getter methods. And after reading a post about C++ constness I was left with more questions than really understanding. A quick search revealed thorough FAQ about const usage in C++. Here I’d like to make a short round-up of it.
How to read const
First of all, read it from right-to-left. For instance int const* const p
would be read as “p is a constant pointer to a constant int”.
A few examples
string const& s
Let’s say you read something like this:
void MyClass::func (std::string const& s);
So reading from right-to-left this would say “s is reference to a constant
std::string”. This implies that func
is not going to modify s
. But keep in
mind that you may have a dangling reference here, especially if you work with
multi-threaded code. If this would be a pointer instead of a reference it is
still possible the pointer to be NULL.
string* const s
void MyClass::func (std::string* const s);
Again, read from right-to-left: “s is a constant pointer to a string”. s
may
be modified (the object itself) but s
may not point to a different object. As
you may have already noticed it’s not that hard to read when you stick to the
“read from right-to-left” rule.
string const* const s
void MyClass::func (std::string const* const s);
This would read “s is a constant pointer to a constant string” and therefore
the function guarantees (respectively the compiler enforces it) that the
content of s
may not be changed nor may s
point to a different object.
string const& const s
A signature as followed
void MyClass::func (std::string const& const s);
is non-sense in term of that references can never be re-assigned and therefore is redundant.
Similar consts
Obviously some signatures mean the same thing:
void MyClass::func (const std::string& s); // you may know this from C
void MyClass::func (std::string const& s);
void MyClass::func (const std::string* s);
void MyClass::func (std::string const* s); // equivalent
void MyClass::func (std::string* const s); // Pitfall: NOT equivalent
Now, it doesn’t matter which version you use but is more a decision you have to make. If previously written code opts for one variant you should for the sake of consistency use the same way. But if you want to use the handy right-to-left rule you may consider the first variant, which in my personal opinion is better for beginners.
Methods that do not change its object
When you write a pure getter method you may want to tell your compiler that the following code may not change the object itself.
std::string const& MyClass::func () const;
The last const indicates that this function may not alter its objects data.
Advanced constness
The mutable keyword
Let’s say that you want to count how many times your getter method was called
but you want to declare your method to be const
. How would you do
that? Well, that’s how it would work:
#include <iostream>
class MyClass
{
private:
std::string s;
mutable int count;
public:
MyClass();
~MyClass();
std::string const& get_string() const;
int get_count();
};
MyClass::MyClass () {
s = "Hello World!";
count = 0;
}
MyClass::~MyClass () {}
std::string const& MyClass::get_string() const {
count++;
return s;
}
int MyClass::get_count() {
return count;
}
int main () {
MyClass o;
for (int i = 0; i < 5; ++i)
{
std::cout << "\"" << o.get_string() << "\"" << \
" was accessed " << o.get_count() << " times." << std::endl;
}
exit (0);
}
Which executed does this:
$ ./mutable
"Hello World!" was accessed 1 times.
"Hello World!" was accessed 2 times.
"Hello World!" was accessed 3 times.
"Hello World!" was accessed 4 times.
"Hello World!" was accessed 5 times.
One more thing: MyClass** → MyClass const**
There is one more thing which is hard to understand and Marshall Cline does a better job at explaining it than I will ever be able to do: Why am I getting an error converting a Foo** → Foo const**?
Final words
A big thank you to Marshall Cline which is a master mind in explaining complex things! And also thank all these people who I bothered so much while writing code.