Professional C__ - Marc Gregoire [141]
int const* ip;
ip = new int[10];
ip[4] = 5; // DOES NOT COMPILE!
Code snippet from Const\Const.cpp
Putting the const before or after the int makes no difference in its functionality.
If you want instead to mark ip itself const (not the values to which it points), you need to write this:
int* const ip = nullptr;
ip = new int[10]; // DOES NOT COMPILE!
ip[4] = 5; // Error: dereferencing a null pointer
Code snippet from Const\Const.cpp
Now that ip itself cannot be changed, the compiler requires you to initialize it when you declare it, either with nullptr as in the preceding code or with newly allocated memory as follows:
int* const ip = new int[10];
ip[4] = 5;
Code snippet from Const\Const.cpp
You can also mark both the pointer and the values to which it points const like this:
int const* const ip = nullptr;
Code snippet from Const\Const.cpp
An alternative but equivalent syntax is the following:
const int* const ip = nullptr;
Code snippet from Const\Const.cpp
Although this syntax might seem confusing, there is actually a very simple rule: the const keyword applies to whatever is directly to its left. Consider this line again:
int const* const ip = nullptr;
Code snippet from Const\Const.cpp
From left to right, the first const is directly to the right of the word int. Thus, it applies to the int to which ip points. Therefore, it specifies that you cannot change the values to which ip points. The second const is directly to the right of the *. Thus, it applies to the pointer to the int, which is the ip variable. Therefore, it specifies that you cannot change ip (the pointer) itself.
const applies to the level of indirection directly to its left.
The reason this rule becomes confusing is an exception: The first const can go before the variable like this:
const int* const ip = nullptr;
Code snippet from Const\Const.cpp
This “exceptional” syntax is used much more commonly than the other syntax.
You can extend this rule to any number of levels of indirection. For example:
const int * const * const * const ip = nullptr;
Code snippet from Const\Const.cpp
Another easy to remember rule to figure out complicated variable declarations: read from right to left. Take for example “int* const ip.” Reading this from right to left gives us “ip is a const pointer to an int.” On the other hand, “int const* ip” will read as “ip is a pointer to a const int.”
const References
const applied to references is usually simpler than const applied to pointers for two reasons. First, references are const by default, in that you can’t change to what they refer. So, C++ does not allow you to mark a reference variable explicitly const. Second, there is usually only one level of indirection with references. As explained earlier, you can’t create a reference to a reference. The only way to get multiple levels of indirection is to create a reference to a pointer.
Thus, when C++ programmers refer to a “const reference,” they mean something like this:
int z;
const int& zRef = z;
zRef = 4; // DOES NOT COMPILE
Code snippet from Const\Const.cpp
By applying const to the int, you prevent assignment to zRef, as shown. Remember that const int& zRef is equivalent to int const& zRef. Note, however, that marking zRef const has no effect on z. You can still modify the value of z by changing it directly instead of through the reference.
const references are used most commonly as parameters, where they are quite useful. If you want to pass something by reference for efficiency, but don’t want it to be modifiable, make it a const reference. For example:
void doSomething(const BigClass& arg)
{
// Implementation here
}
Code snippet from Const\Const.cpp
Your default choice for passing objects as parameters should be const reference. You should only omit the const, if you explicitly need to change the object.
const Methods
Chapter 7 explains that you can mark a class method const. That specification prevents the method from modifying any non-mutable data members of the class.