C++: Const Method Changing its Object

A const method cannot directly change its object in C++. But, there is only so much const-checking that can be done at compile time. It is well known that anything can be hacked in C++ using casting and pointers. I was surprised when I inadvertently created a simple const method that changed its object using just a reference:

#include <vector>
using namespace std;

typedef vector<int> IntVec;

class Foo
{
public:
	void method()
	{
		constMethod( _ivec );
	}

	void constMethod( IntVec& iVec ) const
	{
		iVec.push_back( 10 );
	}

private:
	IntVec _ivec;
};

int main()
{
	Foo foo;
	foo.method();

	return 0;
}

C++: The const Mantra

The const mantra to keep in mind while writing C++ code is to make everything const, that is, as much as possible. Typing 5 extra letters everywhere might seem like extra work, especially to C gurus, but the paybacks of const can be quite significant over time. It is one extra nail hammered into the code to ensure that the code behaves exactly as envisioned in the head of the programmer. Once the vision of the programmer has been converted to code, const helps in ensuring the correctness of this code by pushing the burden of this checking onto the compiler. Thus bugs can be caught much earlier, at compile time. The const code will also behave much nicer with STL and other libraries that give special emphasis to const. They might have faster and/or more robust implementations for const.

Here are some simple guidelines to const the code:

  • Whenever possible, const all input parameters to a method that are passed by reference. That is, const all input reference and pointer variables that will not be modified inside the method. Making a pass-by-value input parameter (like int) as const is typically not useful.
    void FooContainer::append( const FooContainer& );
    
  • Whenever possible, const all methods that do not change the underlying object.
    void FooContainer::display() const;
    
  • Whenever possible, do both of the above.
    void FooContainer::displayWithIndex( const FooVector& ) const;
    
  • Whenever possible, const all variables defined inside the method that will not be modified once initialized. This might sound obvious, but very few people do this since it seems like too much work. The benefits start to pay off once you start seeing the immutable (const) and mutable moving parts churning inside the engine of the method by just glancing over it. It is like wearing 3D glasses, yes, get the extra dimension to your vision! 😉
    void FooContainer::display() const
    {
        const int max = size();
        // ***
    }
    
  • Whenever possible, use const_iterator, cbegin and cend when using STL. cbegin and cend are introduced in C++0X and you can use them if you are using Visual C++ 2010.
    for ( FooVector::const_iterator i = fooVec.cbegin(); i != fooVec.cend(); ++i )
    { /***/ }
    

Note that injecting your code with the const vaccine will have some initial side-effects. The const introduced in one method starts to spread virally through your code, giving const related compile errors. This forces you to re-think the assumptions of your code, re-factor and re-write a lot of your code. Yes, this is good for you and your code. Go gain your const-vision! 😀