C++ 成员函数末尾加 const 到底有什么用?常量成员函数深度剖析
一、基础定义
class Person {
public:
// 普通成员函数
void show() {
age = 100; // 可以修改成员变量
}
// 常量成员函数:函数括号后加 const
void print() const {
// age = 100; // 编译报错,禁止修改成员变量
}
private:
int age = 18;
};
语法规则:写在 () 之后、{} 之前,修饰整个成员函数,称为常量成员函数(const member function)。
二、底层原理:this 指针被 const 修饰
成员函数内部隐含 this 指针:
-
普通成员函数:
Person* const this-
this 本身不可改,但可以通过
this->修改成员变量
-
-
const 成员函数:
const Person* const this-
this 指针指向的对象是常量对象,不能通过 this 修改任何成员
-
// void print() const
void print(const Person* const this) {
this->age = 20; // 非法,*this 是 const
}
三、三大核心作用
1. 限制函数内不能修改类的成员变量
const 成员函数内:
-
不能给普通成员赋值
-
不能调用非 const 成员函数(非 const 函数可能修改对象,不允许)
-
可以读取成员变量、调用其他 const 成员函数
void print() const {
age = 30; // 报错:const函数不能修改成员
show(); // 报错:show是非const函数,禁止调用
}
2. 常量对象只能调用 const 成员函数
用const修饰的对象(常量对象),语法强制只能调用带 const 的成员函数:
int main() {
const Person p; // 常量对象
p.print(); // 合法,print是const函数
p.show(); // 编译报错,show无const,可能修改常量对象
return 0;
}
这是最常用场景:保证常量对象不会被意外修改。
3. 提升代码可读性、规范语义
标记该函数只读、不会改动对象状态,阅读代码一眼就能区分:
-
无 const:读写函数,可能修改成员
-
尾部 const:纯查询函数,只读取数据
四、特殊情况:mutable 突破 const 限制
如果某个成员变量需要在 const 函数中修改,加mutable关键字修饰成员:
class Person {
public:
void print() const {
cnt++; // 合法,mutable变量不受const函数约束
}
private:
int age = 18;
mutable int cnt = 0; // 可变成员,日志/计数器常用
};
适用场景:缓存、访问计数、调试标记等不影响对象核心状态的变量。
五、重载:const 与非 const 构成函数重载
同一个函数名可以同时写 const 和非 const 两个版本,编译器自动匹配调用:
-
普通对象 → 优先调用非 const 版本
-
const 对象 /const 引用对象 → 调用const 版本
#include <iostream>
using namespace std;
class Person {
public:
string& getName() {
cout << "调用非const版本" << endl;
return name;
}
const string& getName() const {
cout << "调用const版本" << endl;
return name;
}
private:
string name = "小明";
};
int main() {
Person p;
p.getName(); // 非const对象 → 非const版本
const Person cp;
cp.getName(); // const对象 → const版本
return 0;
}
输出:
调用非const版本
调用const版本
六、返回值搭配 const 的配套规范
const 成员函数一般返回const 引用,防止外部拿到引用后修改内部成员:
// 规范写法 const string& getName() const { return name; } // 错误:返回普通引用,外部可修改私有成员 string& getName() const { return name; }
七、完整可运行综合示例
#include <iostream>
#include <string>
using namespace std;
class Student {
public:
Student(string n, int a) : name(n), age(a) {}
// 非const函数:可修改成员
void setAge(int a) {
age = a;
}
// const成员函数:只读,不能改成员
void info() const {
// setAge(20); // 报错,禁止调用非const函数
cout << "姓名:" << name << " 年龄:" << age
<< " 访问次数:" << visitCnt << endl;
visitCnt++; // mutable允许修改
}
private:
string name;
int age;
mutable int visitCnt = 0; // 计数器,mutable突破const
};
int main() {
Student s("张三", 18);
s.info();
s.setAge(20);
s.info();
const Student cs("李四", 19);
cs.info(); // const对象只能调用info() const
// cs.setAge(22); // 编译报错
return 0;
}
运行输出:
姓名:张三 年龄:18 访问次数:0
姓名:张三 年龄:20 访问次数:1
姓名:李四 年龄:19 访问次数:0
八、常见误区总结
-
误区:const 修饰返回值
-
错:
const void func()修饰返回值 -
对:
void func() const修饰 this 指针、对象只读
-
-
误区:const 函数完全不能修改任何变量
-
错:mutable 修饰的成员可以修改
-
-
误区:常量对象能调用普通成员函数
-
错:常量对象仅允许调用尾部带 const 的成员函数
-
-
误区:全局 / 静态函数能加尾部 const
-
错:只有普通类成员函数可以加,静态成员无 this 指针,不能写尾部 const
-
九、总结
-
本质:函数内
this变为const Person* const,禁止修改对象成员; -
权限约束:const 对象只能调用 const 成员函数;
-
语义区分:标记函数为只读查询接口,代码更安全规范;
-
扩展:mutable 可让特定成员在 const 函数内修改;const / 非 const 可成对重载适配不同对象。
补充:和之前流运算符知识点联动
重载operator<<时,自定义类输出函数为友元全局函数;如果类内部有读取私有成员的查询接口,建议全部写成尾部 const 成员函数,保证常量对象也能正常输出。
// 配合const成员函数的流重载
class Student {
public:
int getAge() const { return age; } // const查询接口
private:
int age = 18;
friend ostream& operator<<(ostream& os, const Student& s);
};
ostream& operator<<(ostream& os, const Student& s) {
os << s.getAge(); // 传入const引用,只能调用const成员
return os;
}更多推荐

所有评论(0)