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
constto 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* constpointer - 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
thispointer in aconstqualified member function is aconsttype, andthisis inherently related to an instance of a class staticdata 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 ofthisisconst CTextBlock*withoutmutableadded. - Think of static data member
staticMemberasCTextBlock::staticMember, so there’s no constraints fromconsttypethispointer.
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.