Useful tips on using const
in C++.
The const
keyword allows you to specify a semantic constraint and compilers will enforce that constraint. It is remarkably versatile:
- outside of class, you may use it for constants at global or namespace scope, as well as for objects declared static at file, function or block scope
- inside classes, you may use it for both static and non-static data members, for pointers
- when declaring functions, you may also refer
const
to function’s return value, function parameters, and, for member functions, to the function as a whole
1. const
pointer
|
|
If the const
appears to the left of the asterisk, what’s pointed to is constant; if the word const
appears to the right of the asterisk, the pointer itself is constant. It’s helpful to read pointer declarations right to left: const char * const p
reads as “p is a constant pointer to constant chars”.
2. const
iterator
STL iterators are modeled on pointers. Treat it like this:
- iterator ->
T*
pointer - const iterator ->
T* const
pointer - const_iterator ->
const T*
pointer
|
|
3. const
function return value
Generally speaking, having a function return a constant value is inappropriate, but sometimes doing so may reduce implicit errors out of incidence without giving up safety or efficiency.
|
|
Declaring the return value of operator*
to be const prevent typos like this:
|
|
4. const
function parameters
const
parameters act just like local const
objects. Unless there’s a need to modify aparameter or local object, be sure to declare it const
, for it may save you from annoying errors like if (a = b)
above.
5. const
member functions
The purpose of const
on member functions is to identify which member functions may be invoked on const
objects, which benefits us for:
- making the interface of a class easier to understand (which functions may modify an object and which may not)
- making it possible to work with
const
-qualified objects (which makes up a very important C++ feature: overloading member functions differing only in their constness)
5.1 Bitwise constness vs. Logical constness
5.1.1 Bitwise constness
Bitwise constness: a member function is const if and only if it doesn’t modify any of the object’s data members (excluding those that are static). This is also C++’s definition of constness
However, member functions that don’t act very const pass the bitwise test, such as a function including a char* const
pointer manipulating a char*
type class member:
|
|
Since operator[]’s implementation doesn’t modify pText in any way, compilers will happily generate code after verifying that it is indeed bitwise const. However, when you create a constant object with a particular value and invoke only const
member functions on it, you can still change its value:
|
|
To solve this problem, we may store data as a string instead of communicating through a C API char*:
|
|
Remeber the C++ feature mentioned above? By overloading operator[] and giving the different versions different return types, we can handle const and non-const TextBlock objects differently:
|
|
5.1.2 Logical constness
Logical constness: a const member function might modify some of the bits in the object on which it’s invoked, but only in ways that clients cannot detect.
A typical example of logical constness shows in such a scenario: say we want to cache the length for a CTextBlock object, and we define it like as
|
|
Bitwise constness test fails and compilers complains due to the assignment to textLength and lengthIsValid, but it is supposed to be valid for const
CTextBlock objects. The solution is to take advantage of C++’s keyword mutable
, which frees non-static data members from the constraints of bitwise constness:
|
|
According to the mutable’s definition, you may notice that const
member function will not check bitwise constness for static data members. This is because following facts:
- The
this
pointer in aconst
qualified member function is aconst
type, andthis
is inherently related to an instance of a class static
data members are not related to a class instance- For non-static data member
lengthIsValid = true;
, think of it asthis->lengthIsValid = true;
, which is not compilable when the type ofthis
isconst CTextBlock*
withoutmutable
added. - Think of static data member
staticMember
asCTextBlock::staticMember
, so there’s no constraints fromconst
typethis
pointer.
5.2 Avoiding duplication in const
and non-const
member function
There are two versions of operator[] in class TextBlock, which is duplication and tempts us to have one version of operator[] call the other one. Although generally speaking casting is a bad idea, here we may find enough reasons to justify its usage so long as we use it properly (note that we call const
version in non-const
version, not the other way around!):
|
|
It’s definitely worth knowing this technique of implementing a non-const
member function in terms of its const
twin, although the syntax is somehow ungainly.