Const is a keyword originated in the C programming language that had its meaning expanded in C++. The original goal of const in C declarations is to allow for the use of non-modifiable data. The most common example is manipulating string data that is stored in read-only memory. Since it is not possible to modify such data, C has used the const modifier to indicate that these memory areas are immutable.
Later, const has also been used in C++ member functions to express the capacity of objects to apply such functions to a const object. As such, it indicates the ability of objects to behave in non-mutable ways.
First, lets state that there are some advantages in using const whenever possible. The biggest advantage is in making clear the intention of the developer: such member functions are supposed to let the object in the same state. However, as software designers we also need to realize the using const in this way may introduce a few issues.
While this use of const seems just like a logical extension of the original concept, a number of problems may result from this seemingly useful and ingenious generalization. In a nutshell, when considering the possible costs, one starts to realize that the advantages of this feature are not enough to balance the problems arising from using it.
A big concern with const methods is that the const declaration makes each class to behave in two separate ways: a const and a non-const version. This makes it more difficult to reason about the responsibilities of the class, because they can change with different types of use. You may think about this const/non-const split as analogous to the use of ifdef inside code: you need to apply different tests to check that each case of the ifdef is still true.
Similarly, a test case for classes with const member functions has to take in consideration the different behavior arising in each situation. This is especially true because the same member function in a class can be declared differently with one version that is const is another that is non-const.
As bad as it may seem, given the design of C++, if you want to use standard libraries such as the STL then you have no choice. You need to use const whenever required, because the use of const contaminates other parts of your code. If a library expects const and your code doesn’t, you will see frequent compilation problems.
Const as a memory specifier
As mentioned earlier, there are certainly areas in which const is necessary in a language such as C++. The most common place is when constant strings are involved. Since there is no high level type for strings, one has to use const to express that an array of characters shouldn’t be modified. This is the case, for example, when string constants are used.
However, for other types there is little advantage in using const. First of all, const is a storage specification, not a high level description of the class capabilities. Essentially you’re saying that the memory used by the class is in a place where it shouldn’t be modified. Using const for anything else is just an invitation for trouble. For example, there is the strange “const cast” used to remove consents from a pointer. It is clear that this is necessary only because of the non-stadard things people are doing with const. The const-ness of a pointer should not be removed just because we want it, since what will happen when the memory is truly non-modifiable?
Using const then equates to use a low-level memory specification to determine the behavior of a high level entity.
Defining Mutable Classes
Some people believe that const should be used because it can determine if an object is modifiable or not. That is a mistake. Modifiability is a feature of a class, not of a single object. Therefore, we shouldn’t have methods that behave in one way if an object is const and in another otherwise.
Smalltalk solves these issues by creating two separate concepts instead of trying to conflate the two different ideas into the same class. For example, an Array in Smalltalk is immutable. However, you can create a MutableArray to get a version that can be modified. No compiler magic is necessary for this to work, and the decision is much easier for the developer: you choose one of the classes de-pending on what you want to do with the array.
The best thing to do is to clear our minds about if a class is modifiable or not, instead of designing a mix of the two. With C++ const, one needs to frequently evaluate a method and say if it is const or not (both versions can also coexist). This leads to bad de-signs, where what is modifiable is left at the whim of the program-mer when adding the latest member function. A better design would come from deciding if this is a mutable or immutable class. Then make sure that the way to class interacts with others satisfy the contract initially established.