Application of const and volatile in instance member functions

There are almost no restrictions on the scope of use of const and volatile
const or volatile can appear after the parameters of the instance member function, both of which are used to modify the object pointed to by the implicit parameter this of the function.
const appears after the parameter list of the instance function object, indicating that the object pointed to by this is a read-only object that cannot be modified.
However, you can modify the non-read-only static data members of the object pointed to by this.
const, volatile, const volatile appears after the instance function member parameter list, if these instance function objects have implicit parameters this is different, it can be regarded as an overloaded function.
When calling an instance function member, the compiler will match the type of the actual parameter with the type of this, thereby calling the most appropriate instance function member.
Ordinary variables or objects that can be read/written should call instance function members without const or volatile after the parameter list.
const objects should call instance function members with const after the parameter list.
volatile objects should call instance member functions with volatile after the parameter list
Note that if there is no corresponding instance function member when calling, an error will be reported.

Let’s take a look at an example. This example all calls the corresponding function in a matching manner.

#include<iostream>
using namespace std;
class A {<!-- -->
int a;
const int b;
public:
int f() {<!-- -->
cout << "f() " << endl;
a + + ;
//b + + ; cannot be modified
return a;
}
int f()volatile {<!-- -->
cout << "volatile f()" << endl;
a + + ; // You can modify the instance member data member of this class volatile int a
//b + + ;
return a;
}
int f() const volatile {<!-- -->
cout << "f() const volatile " << endl;
// a + + ; is also wrong
return a;
}
int f()const {<!-- -->
cout << "f() const " << endl;
// a + + ; is also wrong
return a;
}
A(int x) :b(x) {<!-- -->
a = x;
}
};
A w(3); // This is a writable object
const A x(6); // This is a readable object
volatile A y(6); // Define volatile objects
const volatile A z(8); // Define a readable volatile object
int main() {<!-- -->
w.f();
x.f();
y.f();
z.f();
}

The final result is as follows:

Let’s analyze the four function members f() above. These four function members are overloaded functions. Their display parameter list does not define any formal parameters, but the type of the implicit formal parameter this Different, this is used to point to the object of the current function.

Let’s think about it in depth. If there is no perfect match, will the compiler fall back to calling the second matching function?
Look at the code below. I commented both fun() const and fun() volatile to see how the situation changes.

#include<iostream>
using namespace std;
class A {<!-- -->
int a;
const int b;
public:
int f() {<!-- -->
cout << "f() " << endl;
a + + ;
//b + + ; cannot be modified
return a;
}
//int f()volatile {<!-- -->
// cout << "volatile f()" << endl;
// a + + ; // You can modify the instance member data member of this class volatile int a
// //b + + ;
// return a;
//}
int f() const volatile {<!-- -->
cout << "f() const volatile " << endl;
// a + + ; is also wrong
return a;
}
//int f()const {<!-- -->
// cout << "f() const " << endl;
// // a + + ; is also wrong
// return a;
//}
A(int x) :b(x) {<!-- -->
a = x;
}
};
A w(3); // This is a writable object
const A x(6); // This is a readable object
volatile A y(6); // Define volatile objects
const volatile A z(8); // Define a readable volatile object
int main() {<!-- -->
w.f();
x.f();
y.f();
z.f();
}

The running results are as follows

We can see that const A x(6) and volatile A y(6) both called f( because they could not find the most suitable matching function. ) const volatile

Let’s look at another code and comment out only the f() const volatile function to see how

#include<iostream>
using namespace std;
class A {<!-- -->
int a;
const int b;
public:
int f() {<!-- -->
cout << "f() " << endl;
a + + ;
//b + + ; cannot be modified
return a;
}
int f()volatile {<!-- -->
cout << "volatile f()" << endl;
a + + ; // You can modify the instance member data member of this class volatile int a
//b + + ;
return a;
}
//int f() const volatile {<!-- -->
// cout << "f() const volatile " << endl;
// // a + + ; is also wrong
// return a;
//}
int f()const {<!-- -->
cout << "f() const " << endl;
// a + + ; is also wrong
return a;
}
A(int x) :b(x) {<!-- -->
a = x;
}
};
A w(3); // This is a writable object
const A x(6); // This is a readable object
volatile A y(6); // Define volatile objects
const volatile A z(8); // Define a readable volatile object
int main() {<!-- -->
w.f();
x.f();
y.f();
z.f();
}

Discover

Reason for error

We will call f() const annotation on the basis of just now

#include<iostream>
using namespace std;
class A {<!-- -->
int a;
const int b;
public:
int f() {<!-- -->
cout << "f() " << endl;
a + + ;
//b + + ; cannot be modified
return a;
}
int f()volatile {<!-- -->
cout << "volatile f()" << endl;
a + + ; // You can modify the instance member data member of this class volatile int a
//b + + ;
return a;
}
//int f() const volatile {<!-- -->
// cout << "f() const volatile " << endl;
// // a + + ; is also wrong
// return a;
//}
//int f()const {<!-- -->
// cout << "f() const " << endl;
// // a + + ; is also wrong
// return a;
//}
A(int x) :b(x) {<!-- -->
a = x;
}
};
A w(3); // This is a writable object
const A x(6); // This is a readable object
volatile A y(6); // Define volatile objects
const volatile A z(8); // Define a readable volatile object
int main() {<!-- -->
w.f();
x.f();
y.f();
z.f();
}

Let’s see how to report an error

Let’s do a final experiment and just comment out the f() function to see what happens.

#include<iostream>
using namespace std;
class A {<!-- -->
int a;
const int b;
public:
//int f() {<!-- -->
// cout << "f() " << endl;
// a + + ;
// //b + + ; cannot be modified
// return a;
//}
int f()volatile {<!-- -->
cout << "volatile f()" << endl;
a + + ; // You can modify the instance member data member of this class volatile int a
//b + + ;
return a;
}
int f() const volatile {<!-- -->
cout << "f() const volatile " << endl;
// a + + ; is also wrong
return a;
}
int f()const {<!-- -->
cout << "f() const " << endl;
// a + + ; is also wrong
return a;
}
A(int x) :b(x) {<!-- -->
a = x;
}
};
A w(3); // This is a writable object
const A x(6); // This is a readable object
volatile A y(6); // Define volatile objects
const volatile A z(8); // Define a readable volatile object
int main() {<!-- -->
w.f();
x.f();
y.f();
z.f();
}

The following error was found:

To sum up, the overall priority is as follows:
Nothing > const and volatile > const volatile
Let me explain, if an object does not contain const and volatile, then it will be called first if it does not contain const and volatile > function, if not, call one of f() const or f() volatile. If both exist, the compiler does not know which one to call. , an error will be reported. If neither f() const nor f() volatile exists, then f() const volatile will be called.
The same is true if the object is of type const. First look for f() const , and then call f() const volatile if it cannot be found.

And add a small point, const and volatile cannot appear after the parameter list of the constructor or destructor, because when constructing or destructing an instance object, the object must It can be modified and must be in a stable state (volatile means it is a volatile object)