You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

Relearning C++: Notes on uses of const qualifier

5 Mar 2015   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
In this post I write down some notes as from my study of C++ on the uses of the important const keyword

The meaning of const type qualifier is to a the variable unchangeable. Because the value of a const object can’t be changed after created, it must be initialized.

The const-ness is just another form of type safety. If ordinary type safety helps us to get systems correct, const correctness does this too.

The benefit of const correctness is that it prevents us from inadvertently modifying something we didn’t expect would be modified. The compiler uses the const info to prevent mistakes and other programmers use it as documentation.

It is very convenient to use const in your program early and often.
The const checking is done entirely at compile-time: there is no run-time space or speed cost.

Named constant

It's the simpler case:

const int constant1 = 23;  

References to const

Strictly, there are no const references. A reference is not an object, so we cannot make a reference itself const. In fact, because there is no way to make a reference refer to a different object, in some sense all references are const. Whether a reference refers to a const or nonconst type affects what we can do with that reference.

On the other hand, a reference to const is a reference that points to a const type.

const int ci = 21;
const int &r = ci; // both reference and underlying object are const

It is important to realize that binding a reference to const to an object says nothing about whether the underlying object itself is const. Because the underlying object might be nonconst, it might be changed by other means.

Pointers to const and const pointer

A pointer to const is a non-constant pointer to constant data; the data pointed to cannot be changed; but pointer can be changed to point to another data. Example sintax:

const int *ptrToConst;  
int const *ptrToConst;  // alternative syntax

On the other hand, a const pointer is a constant pointer to non-constant data; the data pointed to can be changed; but pointer cannot be changed to point to another data.

Of course, a const pointer, like any other const object, must be initialized, and once initialized, its value may not be changed. Example:

int num = 0;
int *const numPtr = &num  // numPtr will always point to num

The fact that a pointer is itself const says nothing about whether we can use the pointer to change the underlying object. Whether we can change that object depends entirely on the type to which the pointer points.

It's used the term top-level const to indicate that the pointer itself is a const. When a pointer can point to a const object, we refer to that const as a low-level const.

Finally, we can have a constant pointer to a constant type. Example:

int const * const constPtrToConst= #

Use of const in functions

Use in function parameters

It's particularly useful and typical to declare function parameters as const references (I explain the reasons in my post on functions). This way:

bool myFunction(const myObj& obj);

const return type

It's useful to use const for object return type to prevent it to be used as lvalue. A const return object cannot be a lvalue but a non-const return object can.

For example, we can return a pointer (constant or not) to const objects typically implemented as pointers -strings, arrays- that the program could otherwise try to alter and crash. This way, the attempt to alter inalterable values will be detected during compilation. For example:

const char *Function1()
{ return "Some text";}

The compiler would spott a error if the code try to execute this line: Function1()[1]=’a’;.

Use of const in classes

Const member function

When we use const objects, we don't want to call methods that can change the object, so we need a way to tell the compiler which methods can be safely called. This (member) methods, known as const member functions, are the only ones that can be called on a const object. Note, by the way, that only member methods make sense as const methods. Note that const methods receive a const implicit pointer to the object.

Otherwise, a non-const object can invoke both const and non-const member function.

The way to declare that a function is safe for const objects is simply to mark it as const:

int ClassName::constMethod() const

The trailing const on member function should be used to mean the method won’t change the object’s abstract/logical state. That is not the same that how it is internally implemented. This is a quite complicated topic and it is well explained here: How can it help me design better classes if I distinguish logical state from physical state? - Const Correctness, C++ FAQ

Const member functions that returns part of the this object’s logical/abstract state must return a const pointer (or reference) to that part.

Const member overloading

If a function is overloaded with a const and a non-const version, a const object will match with a const member function. A non-const object will match with a non-const function.

For example, the at() function of string class has two versions:

char & at (size_t pos);              // non-const member function
const char & at (size_t pos) const;  // const member function

A const string object will invoke the const version, which returns a const char & that cannot be used a lvalue.

Const iterator

We can use const iterators if we want to prevent modifying the elements in a collection object (const or non-const). Const iterators use in the STL is very simple: just append "const_" to the type of iterator we desire:

const_iterator itr = vec.begin()

mutable and const_cast

mutable and const_cast keywords allow to change const-ness of objects.