22.5 The ''const'' function declaration

When you look at documentation on classes, you see a lot of pesky const. What's all that about? Well, first off let's say that if they confuse you too much, it would in fact be OK to just leave them all out. But there is a reason for them. It's generally considered a good idea to put const after the declaration of any function with does not change the values of a class's private fields.

int Func(SomeClass input)const; //Doesn't change the caller class 
int Func(SomeClass input); //May change the caller class 

Meticulous programmers (and that's what we should all want to be!) use const as a way of telling the compiler to warn you if it finds anything in that function's code which changes a private class member. If you don't bother to put the const into your class and a meticulous programmer uses your class in another class definition where he or she has const, then there will in fact be trouble, as the compiler will be scared to use your non-const function in a const function definition.

There are some savage gotchas connected with the use of the const after a function prototype. These have to do with the fact that a C++ compiler internally 'name-mangles' function names so that a function has a completely different name according to whether it's a const or not. We could put the two Func prototypes above into a class and C++ would compile as if these were completely different functions.

This has two unpleasant consequences. First of all, whether the implementation of a function includes the word const or not depends upon the prototype. Otherwise you get a compiler error. That is, if either or both of the two different prototypes above were in a cMyClass, they'd be respectively implemented like this.

int cMyClass:Func(cSomeClass input)const{ .... } 
int cMyClass:Func(cSomeClass input){ .... } 

The second unpleasant consequence is much worse. Suppose you declare a virtual function in a base class and then redeclare it in a child class. (If you're hazy about what virtual functions are, look down at the description in Section 22.10.) If the const declarations of the two functions don't match, then the virtual base class function won't call the child class function. This happened to the author recently with some code like this.

class cSprite 
    virtual void draw()const; 
class cPolygon : public cSprite 
    void draw(); 

When one sets a cSprite* psprite = new cPolygon() and calls psprite->draw(), one keeps getting the cSprite::draw instead of the cPolygon::draw. This is because C++ viewed our draw() in cPolygon as a new function completely different from the draw()const in cSprite.

Starting to use const is an all-or-nothing decision. That is, if you start using const in one file, then the compiler will make you use it in every related file. The reason for this is that if you try, say, to give a class Careful a const accessor method which uses the non-const accessor member of a Casual class member, then the compiler will balk. Here's an example:

class Casual 
    int _val; 
    int val(){return _val;} 

class Careful 
    Casual _casual_member; 
    int val() const {return _casual_member.val();} 
    /* Won't compile, will give error like "non-const function 
    called on const object." */ 

Putting const declarations into your code is a bit of a hassle, but if you plan to have others use your code, you have to do it. Why? Because even if you like to be casual, your user may very well be careful, and, as just explained, when a careful class tries to use a casual class method there can be a conflict.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference