一、const 成员函数的核心定义
const修饰成员函数时,要写在函数参数列表后、函数体前(有virtual则在virtual后),其核心作用是:保证该成员函数不会修改当前对象的任何非静态成员变量,且只能调用其他 const 成员函数。
语法格式:
cpp
// 声明(类内)
返回值类型 函数名(参数列表) const;
// 定义(类外)
返回值类型 类名::函数名(参数列表) const {
// 函数体
}
二、为什么需要 const 成员函数?
核心目的是保证 "常量对象" 的只读性------ 当一个对象被声明为const(常量对象)时,编译器要求它只能调用const成员函数,防止对象被意外修改。
先看一个没有 const 成员函数的反例:
cpp
#include <iostream>
using namespace std;
class Person {
public:
int age = 20;
// 普通成员函数:可修改成员变量
void showAge() {
cout << "Age: " << age << endl;
// 即使没修改,编译器也认为这个函数"可能修改"对象
}
};
int main() {
// 常量对象:不允许被修改
const Person p;
p.showAge(); // 编译报错!常量对象不能调用普通成员函数
return 0;
}
报错原因:编译器无法确定showAge()是否会修改p,因此禁止常量对象调用普通成员函数 ------ 而const成员函数就是用来告诉编译器:"这个函数绝对不会修改对象,放心让常量对象调用"。
三、const 成员函数的使用规则(核心)
- 基础规则:const 成员函数不能修改非静态成员变量
cpp
#include <iostream>
using namespace std;
class Person {
public:
int age = 20;
static int total = 0; // 静态成员变量
// const成员函数:只读,不能修改非静态成员
void showAge() const {
cout << "Age: " << age << endl; // 允许:仅读取
// age = 30; // 编译报错!const函数不能修改非静态成员变量
total = 100; // 允许:静态成员变量不属于具体对象,不受const限制
}
// 普通成员函数:可读写
void setAge(int a) {
age = a; // 允许修改
}
};
int main() {
// 1. 常量对象:只能调用const成员函数
const Person p1;
p1.showAge(); // 正常执行
// p1.setAge(30); // 编译报错!常量对象不能调用普通成员函数
// 2. 普通对象:既可以调用const成员函数,也可以调用普通成员函数
Person p2;
p2.showAge(); // 正常执行
p2.setAge(30); // 正常执行
return 0;
}
- 进阶规则:const 成员函数只能调用其他 const 成员函数
cpp
class Person {
public:
int age = 20;
void func1() const {
func2(); // 编译报错!const函数不能调用普通成员函数
func3(); // 允许:调用其他const成员函数
}
void func2() {
// 普通成员函数
}
void func3() const {
// const成员函数
}
};
原因:普通成员函数 "可能修改对象",而 const 成员函数承诺 "不修改对象",因此不能调用有修改风险的普通成员函数。
- 例外:mutable 成员变量可被 const 函数修改
mutable(可变的)修饰的成员变量,突破 const 的限制 ------ 即使在 const 成员函数中,也能修改mutable成员(用于存储 "逻辑上不影响对象常量性" 的状态,比如缓存、计数):
cpp
#include <iostream>
using namespace std;
class Person {
public:
int age = 20;
mutable int visitCount = 0; // 可变成员:记录访问次数
void showAge() const {
visitCount++; // 允许:mutable成员可被const函数修改
cout << "Age: " << age << ",访问次数:" << visitCount << endl;
}
};
int main() {
const Person p;
p.showAge(); // 输出:Age: 20,访问次数:1
p.showAge(); // 输出:Age: 20,访问次数:2
return 0;
}
四、底层原理:const 修饰的是 this 指针
const 成员函数的本质,是修改了this指针的类型:
普通成员函数中,this的类型是:类类型* const(指针本身不可改,指向的对象可改);
const 成员函数中,this的类型是:const 类类型* const(指针本身不可改,指向的对象也不可改)。
编译器视角的伪代码对比:
cpp
// 普通成员函数:void showAge(Person* const this)
void Person::showAge() {
this->age = 30; // 允许:this指向的对象可改
}
// const成员函数:void showAge(const Person* const this)
void Person::showAge() const {
this->age = 30; // 报错:this指向的对象是const,不可改
}
核心:const成员函数的this指针是 "指向 const 对象的 const 指针",因此无法通过this修改成员变量 ------ 这也是 const 函数不能修改非静态成员的底层原因。
五、const 成员函数的重载
const 成员函数可以和普通成员函数构成重载,编译器会根据调用对象是否为 const,选择对应的版本:
cpp
#include <iostream>
using namespace std;
class Person {
public:
int age = 20;
// 普通版本:供普通对象调用
void showAge() {
cout << "普通版本:Age = " << age << endl;
}
// const版本:供常量对象调用
void showAge() const {
cout << "const版本:Age = " << age << endl;
}
};
int main() {
Person p1; // 普通对象
p1.showAge(); // 调用普通版本
const Person p2; // 常量对象
p2.showAge(); // 调用const版本
return 0;
}
总结
核心作用:const 成员函数承诺 "不修改对象的非静态成员变量",让常量对象可以安全调用;
关键规则:
不能修改非静态成员变量(mutable成员除外);
只能调用其他 const 成员函数;
可与普通成员函数构成重载,编译器根据对象是否 const 选择版本;
底层本质:const 修饰的是this指针,使其变为const 类类型* const,限制对对象的修改。
开发建议:只要成员函数不修改对象状态,就声明为 const 成员函数 ------ 这是 C++"常量正确性" 的最佳实践,能让代码更健壮、更易维护。