We talk about the relevance of the const qualifier and best practices one should adopt as a
C++ developer.
In C++, the const qualifier can be applied for elements at various levels
- global-scope
- class-scope and
- local (function) scope for functions, variables, and class objects.
Here in this discussion our focus will be on the usage and importance of the const qualifier
when applied for
- class objects and
- class member functions as a C++ developer
Why should we use const on class objects and thereby on certain member functions of a
class?
Let us consider the following example/scenario,
We as an application developer plan to consume a class library developed by another team
or vendor, and we do not have the privileges of editing or modifying this class library. This
class also does not follow the const correctness aspect.
The class looks as follows…
#include<iostream>
using namespace std;
//A library class
class MyClass
{
private:
int a,b;
public:
MyClass(int x=0, int y=0):a(x),b(y)
{
// -- Constructor business
}
void input() //Does a write operation
{
cout << "enter 2 nos.." << endl;
cin >> this->a >> this->b;
}
void print() //Does a read-only operation
{
cout << "The value of a=" << a << ", and b=" << b << endl;
}
~MyClass()
{
// -- Destructor's business
}
};
Given the above class, let us consider two different scenarios as to how a class consumer
would go about consuming the above class in his/her application code.
//consumer code
/*
Scenario-1:
- The class consumer wishes to create an instance with an
initial value of 10 & 20.
- Later, at some point in the application wishes to modify
the object's value
*/
int main()
{
MyClass ob1(10, 20);
ob1.print(); // object read successful
//----
ob1.input(); // object modification successful
ob1.print();
return 0;
}
Now, the second scenario:
//consumer code
/*
Scenario-2:
- The class consumer wishes to create an instance with an
initial value of 10 & 20.
- And desires this initial state shall prevail throughout the
object's life-time, any attempts to modify should not be
permitted.
*/
int main()
{
const MyClass ob1(10,20); // const? because it should be read-only
ob1.input(); // call fails, as the function attempts modification
ob1.print(); // call fails here as well though function only read's
return 0;
}
The call to ‘print’ is an error that would be issued to the class consumer for no fault of his.
This is so, because the class library vendor has overlooked the ‘const’ correctness aspect.
We say,
- A const object would only call const methods
- An object that is not const call any method, whether const or not a const
Let us dive a bit deep to understand this issue or error in the first place. The following
snippet illustrates as to what the compiler translation or interpretation of our class code
internally looks like as a binary.
class MyClass
{
public:
/* [COMPILER TRANSLATION] */
void input(); // void input(MyClass* const this);
void print(); // void print(MyClass* const this);
};
int main()
{
const MyClass ob1(10, 20);
//The compiler translation ==> MyClass::input(&ob1), thus ===> MyClass::input(const MyClass* )
ob1.input();
// The compiler translation ==> MyClass::input(&ob1), thus ===> MyClass::input(const MyClass* )
ob1.print();
}
Note, the hidden formal parameter ‘this’ is a pointer to a READ-WRITE ‘ MyClass ‘ type, hence
can only accept the address of a READ-WRITE ‘ MyClass ‘ type and not a READ-ONLY type
‘const MyClass *’ type. Hence the error.
So, the ‘MyClass:print’ method’s hidden formal parameter must be a pointer to a READ-ONLY
type. This is when or why the class developer should request the compiler to consider or
treat the hidden formal parameter ‘this’ as READ-ONLY for the ‘MyClass:print’.
The following changes are necessary on the class end to make this happen, qualify both
declaration as well as the definition of the ‘print’ method as ‘const’ on the right-hand-side.
class MyClass
{
public:
/* [COMPILER TRANSLATION] */
void input(); // void input(MyClass* const this);
//The method is qualified as 'const'
void print() const; // void print(const MyClass* const this);
return 0;
};
int main()
{
const MyClass ob1(10, 20);
ob1.input(); // Error, not allowed - that is what we wanted
ob1.print(); // OK, will compile
return 0;
}
Note:
- Once a method is marked ‘const ‘, we cannot modify the class data members in the
functions scope. - Avoid qualifying the constructor(s) and destructor method as ‘const ‘, doing so is an
error for the following reasons.
The effect of the ‘const ‘ qualifier on an object only takes place after/during the
epilogue phase of the constructor call and nothing earlier. And the same gets removed or
neutralized during the prologue phase of the destructor call.
So, to conclude the effect the ‘const’ qualifier on an object is only between these two
transitions.
The following image illustrates the same.

What are the best practices and thereby which candidate must be qualified as ‘const ‘ in a
C++ code in general ?
FIRST-LEVEL of const correctness:
- If there is any member function only doing a read-only business on object’s data, qualify
such functions as ‘const ‘.
SECOND LEVEL of const correctness:
The reason for copy constructors formal parameter being declared as ‘const ‘:-
- As a class library developer, if we happen to define our own custom copy constructor in
a class, then we have to ensure that our copy constructor is ready to receive either a read-
write or a read-only object from the consumer end.
Note: On the formal parameter received by the copy constructor, there is a READ-ONLY
operation that is being attempted on the formal parameter. Thereby, our custom copy
constructor should welcome either a non-const object or a const object.
Here ‘x’ is an alias to a READ-WRITE CA kind, acts as an alias only to non-const objects,
which is not good.
MyClass(MyClass& x)
{
//...
}
Here ‘x’ is an alias to a READ-ONLY MyClass kind, acts as an alias to both const as well as
non-const objects, a good idea.
MyClass(const MyClass& x)
{
//...
}
THIRD-LEVEL of const correctness: –
class MyClass
{
public:
//..
// Formal parameter is not a 'const', OK if we are attempting a write operation
// on the formal parameter inside the function
void compute1(MyClass& ob)
{
//read-write business on 'ob'
}
// On the parameter 'ob' received by the function there is a read-only
// operation inside the function scope, then it is a good practice to qualify
// the formal parameter of this function as 'const.
void compute2(const MyClass& ob)
{
//read-only business on 'ob'
}
};
Hope, by now we have understood the importance or relevance of ‘const’ correctness in a
C++ code.