1. 程式人生 > >Const member functions in C++

Const member functions in C++

pear treat not rul automatic man sel cannot example

Recently, I‘ve started to review and learn C++ techniques. During the learning process, I followed a learn-by-example methodology, which I consider to be very effective. From now on, I‘ll use this blog to explain key points and interesting topics in C++ along with commented example code. In this post, I‘ll clarify how to define and use const member functions in C++.

Const objects can only be accessed by const member functions

Const is an important concept in C++. It is not only used to add constraints to variables, function arguments and return values, but also applied as suffix to class member functions, which are called const member functions. With this const keyword explicitly added, the compiler is told that the class member variables will not be changed during the execution of the member function. This explicit declaration is mandatory because during compile time, the compiler has no knowledge about the constness of member variables in the function execution.

Const class member functions are mandatory because they are the only interfaces for accessing constant objects. If they are not defined, the following compiler error will usually appear:

error: passing ‘const A‘ as ‘this‘ argument of ‘int& A::GetValue()‘  discards qualifiers [-fpermissive]

This can be understood like this: every class member function has an inherent first argument which is the this pointer pointing to the current object instance:

result_type member_function(A* this_pointer, ...); 

When the const keyword is appended to the member function, it is equivalent to declare:

restul_type member_function(const A* this_pointer, ...);

which automatically makes the current object instance constant. Now it can be seen that accessing const class members using a mutable member function is equivalent to passing a constant object to a member function with mutable object argument. Obviously, such type casting from mutable to const is invalid.

Rules for return value of const member functions

Because all member variables are considered as const by the compiler in the const member function, reference to class member can only be returned as const, otherwise there will be invalid type cast from const to mutable. If the returned value of the const member function is a pointer type class member, the return type should also be const pointer. Then the stored address in the pointer itself cannot be changed, while the value pointed by the pointer can be changed.

Mutable class member functions are mandatory

If mutable version of class member functions is not defined, the const version can still be used to access member variables but in read-only mode. This is because a mutable class member can be converted to a const type, but not vice versa. However, if we want to use the returned member variable as an lvalue, we need mutable version of class member functions. Furthermore, if only mutable class member functions are defined without const version, const objects cannot be accessed anymore with the following error:

In member function ‘int& A::GetValue() const‘:
37:36: error: invalid initialization of reference of type ‘int&‘ from expression of type 
‘const int‘

which is a direct indication that the member variables are automatically treated as const and cannot be casted to mutable types.

Modification of class static variables

Const member functions only ensure that no changes will happen on member variables. Therefore, class static variables, which do not belong to any class instance, can be modified inside const member functions.

Example code

The following code exemplifies the above key points about const member functions in C++ with necessary comments.

  1 // This code demonstrates the correct definition and usage of const member functions.
  2 
  3 #include <iostream> 
  4 
  5 using namespace std;
  6 
  7 class A {
  8 public:
  9     friend ostream& operator<<(ostream& os, const A& a);
 10     
 11     // Class static variables: it can be modified by const member functions.
 12     static int object_num;
 13     
 14     // Default constructor
 15     A() : value(0), pointer(0) { cout << "Number of A objects: " << ++object_num << endl; }
 16     // Constructor
 17     A(int val, int* pt) : value(val), pointer(pt) { cout << "Number of A objects: " << ++object_num << endl; }
 18     
 19     // //////////////////////////////
 20     // Const member access functions
 21     // //////////////////////////////
 22     // Note: if these const member access functions are not defined, const object 
 23     // of A cannot be accessed. Therefore, they are mandatory.
 24     
 25     // Const member function can only return const reference to class member.
 26     const int& GetValue() const { return value; }
 27     // Const member function can only return const pointer which is defined as class member.
 28     // Note: the value pointed by the member pointer can be changed actually.
 29     const int* GetPointer() const { return pointer; }
 30     
 31     // The following definitions are invalid: if they operate on const object instance, they try to 
 32     // conver the const member variables to mutable as the return value, which of course is not valid.
 33     // Const member function can only return const reference to class members.
 34     //
 35     // The generated error message is:
 36     //   In member function ‘int& A::GetValue() const‘:
 37     //     37:36: error: invalid initialization of reference of type ‘int&‘ from expression of type 
 38     //     ‘const int‘
 39     // which is a direct indication that the member variables are automatically treated as const.
 40     // int& GetValue() const { return value; }
 41     // Const member function can only return const pointer to class members.
 42     // Note: the value pointed by the member pointer can be changed actually.
 43     // int* GetPointer() const { return pointer; }
 44     
 45     // //////////////////////////////////////////////
 46     // Non-const version of member access functions.
 47     // //////////////////////////////////////////////
 48     // If non-const version of member access functions are not defined, the const-
 49     // version of member access functions can still be used to access member 
 50     // variables but in read-only mode, this because a mutable value can be converted to a const 
 51     // value, but not vice versa.
 52     // Therefore, if we want to use the returned member variables as lvalue, we need these non-const 
 53     // version member access functions.
 54     int& GetValue() { return value; }
 55     int* GetPointer() { return pointer; }
 56     
 57     // Because const keyword for member functions only applies to class member 
 58     // variables, the value pointed by member pointer can be changed actually, 
 59     // even though the member function is declared as const.
 60     int& GetPointerValue() const {
 61         // This line shows the const member function can really modify the class 
 62         // static variable.
 63         A::object_num = 10;
 64         cout << "A::object_num is overwritten as 10" << endl;
 65         
 66         return *pointer;
 67     }
 68     
 69 private:
 70     int value;
 71     int * pointer;
 72 };
 73 
 74 // Initialize the class object number to zero.
 75 int A::object_num = 0;
 76 
 77 ostream& operator<<(ostream& os, const A& a) {
 78     os << "value=" << a.value << ", " << "pointer value=" << *a.pointer << endl;
 79     
 80     return os;
 81 }
 82 
 83 int main(){
 84     int local_int = 3;
 85     int value1 = 5;
 86     int value2 = 8;
 87     
 88     // Define a mutable object.
 89     A a(value1, &local_int);
 90     cout << "object a is: " << a;
 91     // Change the member of a mutable object.
 92     a.GetValue() = value2;
 93     cout << "object a is changed to: " << a;
 94     
 95     // Define a const object.
 96     const A b(value1, &local_int);
 97     cout << "Const object b is " << b;
 98     // Get the member variable value of a const object. If const version of 
 99     // member access functions are not defined, this is invalid and will produce 
100     // the following error:
101     //   error: passing ‘const A‘ as ‘this‘ argument of ‘int& A::GetValue()‘ 
102     //   discards qualifiers [-fpermissive]
103     // This can be understood like this:
104     // Everyone class member function‘s inherent first argument is the this pointer pointing to the 
105     // current instance, so it has the following form:
106     //   result_type member_function(A* this_pointer, ...)
107     // When the const keyword is appended to the member function, it is equivalent to declare:
108     //   restul_type member_function(const A* this_pointer, ...)
109     // which automatically makes the current object instance constant.
110     // The situation we are facing is trying to pass a constant object to a non-const member function, 
111     // so the type casting from mutable to const is invalid.
112     cout << "Member variable value in b is: " << b.GetValue() << endl;
113     // Modify a const object: this should be invalid.
114     // b.GetValue() = value2;
115     // cout << "Const object b is changed to: " << b;
116  
117     // The following function executes:
118     // 1. Modify the value pointed by member pointer in a const object: this is 
119     // valid even though the member function is const.
120     // 2. Modify the class static variable, which does not belong to the (*this) 
121     // instance and is valid.
122     b.GetPointerValue() = 1;
123     cout << "Const object b is changed to: " << b;
124         
125     return 0;
126 }

Const member functions in C++