2.13.  const Member Functions

[ fromfile: constmembers.xml id: constmembers ]

When a class member function ClassX::f() is invoked through an object objx

objx.f(); 

we refer to objx as the host object.

The const keyword has a special meaning when it is applied to a (non-static) class member function. Placed after the parameter list, const becomes part of the function signature and guarantees that the function will not change the state of the host object.

A good way to look at the way const modifies member functions is to realize that each non-static member function has an implicit parameter, named this, which is a pointer to the host object. When you declare a member function to be const, you are telling the compiler that, as far as the function is concerned, this is a pointer to const.

To explain how const changes the way a function is invoked, we look at how the original C++ to C preprocessor dealt with member functions. Because C did not support overloaded functions or member functions, the preprocessor translated the function into a C function with a "mangled" name that distinguished itself from other functions by encoding the full signature in the name. The mangling process also added an extra implicit parameter to the parameter list: this, a pointer to the host object. Example 2.21 shows how member functions might be seen by a linker after a translation into C.

Example 2.21. src/const/constmembers/constmembers.cpp

#include <QTextStream>
#include <QString>

class Point {
  public:
    Point(int px, int py)
       : m_X(px), m_Y(py) {}

    void set(int nx, int ny) {      1
        m_X = nx;
        m_Y = ny;
    }
    QString toString() const {      2
       // m_X = 5;                  3
        m_Count++;                  4
        return QString("[%1,%2]").arg(m_X).arg(m_Y);
    }
  private:
    int m_X, m_Y;
    mutable int m_Count;            5
};


int main() {
   QTextStream cout(stdout);
   Point p(1,1);
   const Point q(2,2);
   p.set(4,4);                      6
   cout << p.toString() << endl;
   //q.set(4,4);                    7
   return 0;
}

1

C version: _Point_set_int_int(Point* this, int nx, int ny).

2

C version: _Point_toString_string_const(const Point* this).

3

Error: this->m_X = 5, *this is const.

4

Okay, member is mutable.

5

mutable can be changed in a const method.

6

Okay to reassign p.

7

Error! const object cannot call non-const methods.

<include src="src/const/constmembers/constmembers.cpp" href="src/const/constmembers/constmembers.cpp" id="constmemberscpp" mode="cpp"/>


In a real compiler, the mangled names for set and print would be compressed significantly, to save space and hence be less understandable to a human reader. Notice that mutable members can be modified inside const member functions, but regular data members cannot.

You can think of the const in the signature of print() as a modifier of the invisible this parameter that points to the host object. This means that the memory pointed to by this cannot be changed by the action of the print() function. The reason that the assignment x = 5; produces an error is that it is equivalent to this->x = 5;. The assignment violates the rules of const.

Suppose that you need to work with a project that contains classes that do not use const correctly. When you start to add const to member functions, parameters, and pointers that need it, you may find that those changes generare a cascade of compiler errors that prevent you from building the project until const has been correctly added throughout the project. When const has finally been added to all the correct places, you can say that your classes are const correct.