C++ 第二阶段:运算符重载 - 第二节:重载与 const 成员函数
目录
一、const 成员函数的核心概念
1.1 什么是 const 成员函数?
1.2 const 成员函数的语法
1.3 const 成员函数的实现原理
二、运算符重载与 const 成员函数的结合
2.1 为什么需要 const 成员函数重载?
2.2 流输出运算符 << 的 const 重载
2.3 赋值运算符 = 的 const 限制
三、const 成员函数与运算符重载的注意事项
3.1 const 成员函数的语义一致性
3.2 const 成员函数的返回值设计
3.3 const 成员函数与自赋值检测
四、常见运算符重载的 const 版本设计
4.1 下标运算符 []
4.2 流输出运算符 <<
4.3 前置/后置自增运算符 ++
五、常见错误与解决方案
5.1 错误:const 对象调用非 const 成员函数
5.2 错误:const 成员函数修改成员变量
5.3 错误:赋值运算符重载为 const 成员函数
六、总结
6.1 核心要点
6.2 最佳实践
一、const 成员函数的核心概念
1.1 什么是 const 成员函数?
- 定义:
const
成员函数是通过在成员函数声明和定义的末尾添加const
关键字来修饰的函数。 - 作用:
- 保证函数内部不会修改类的成员变量。
- 允许 const 对象调用该函数。
- 防止权限放大问题(避免通过 const 对象调用非 const 函数)。
1.2 const 成员函数的语法
class MyClass {
public:int data;// const 成员函数void print() const {std::cout << data << std::endl; // 合法:data 是只读的// data = 10; // 非法:不能修改成员变量}
};
1.3 const 成员函数的实现原理
const
实际上修饰的是隐含的this
指针,即:void MyClass::print() const {// 等价于 this 是 const MyClass* }
二、运算符重载与 const 成员函数的结合
2.1 为什么需要 const 成员函数重载?
当需要对 const 对象 使用运算符时,必须提供 const 版本的运算符重载函数,否则会导致编译错误。
示例:下标运算符 []
的 const 重载
class Array {
private:int data[10];
public:Array() {for (int i = 0; i < 10; ++i) {data[i] = i * 2;}}// 非 const 版本:允许修改元素int& operator[](int index) {return data[index];}// const 版本:仅允许读取元素const int& operator[](int index) const {return data[index];}
};int main() {const Array arr;std::cout << arr[3] << std::endl; // 调用 const 版本的 operator[]// arr[3] = 100; // 编译错误:const 对象不能调用非 const 运算符return 0;
}
2.2 流输出运算符 <<
的 const 重载
流输出运算符 <<
通常需要以友元函数实现,且参数必须为 const
,以支持 const 对象。
#include <iostream>class Vector2 {
public:int x, y;Vector2(int a, int b) : x(a), y(b) {}// 友元函数实现 << 运算符friend std::ostream& operator<<(std::ostream& os, const Vector2& vec);
};// 定义时必须标记为 const
std::ostream& operator<<(std::ostream& os, const Vector2& vec) {os << "(" << vec.x << ", " << vec.y << ")";return os;
}int main() {const Vector2 v(5, 7);std::cout << v << std::endl; // 输出: (5, 7)return 0;
}
2.3 赋值运算符 =
的 const 限制
- 赋值运算符必须是非 const 成员函数,因为它会修改当前对象的状态。
- 赋值运算符不能重载为 const 成员函数,否则会导致逻辑错误。
class String {
private:char* data;
public:String(const char* str) {data = new char[strlen(str) + 1];strcpy(data, str);}// 赋值运算符重载(非 const)String& operator=(const String& other) {if (this == &other) return *this; // 自赋值检测delete[] data; // 释放旧资源data = new char[strlen(other.data) + 1];strcpy(data, other.data);return *this;}~String() {delete[] data;}
};
三、const 成员函数与运算符重载的注意事项
3.1 const 成员函数的语义一致性
- 运算符重载的 const 版本必须与非 const 版本行为一致,但仅允许读取数据。
- 示例:下标运算符的 const 版本应返回
const T&
,而非 const 版本返回T&
。
3.2 const 成员函数的返回值设计
- 返回值类型:
- const 版本:返回
const T&
或const T
,防止修改数据。 - 非 const 版本:返回
T&
,允许修改数据。
- const 版本:返回
示例:返回值设计对比
class MyContainer {
private:int data[10];
public:// 非 const 版本int& get(int index) {return data[index];}// const 版本const int& get(int index) const {return data[index];}
};
3.3 const 成员函数与自赋值检测
- 在运算符重载中,自赋值检测(如赋值运算符)必须同时适用于 const 和非 const 版本。
- 示例:赋值运算符的自赋值检测逻辑必须包含在函数体内。
四、常见运算符重载的 const 版本设计
4.1 下标运算符 []
- 非 const 版本:返回
T&
,允许修改元素。 - const 版本:返回
const T&
,仅允许读取元素。
class Array {
private:int data[10];
public:int& operator[](int index) {return data[index];}const int& operator[](int index) const {return data[index];}
};
4.2 流输出运算符 <<
- 必须以友元函数实现,且右操作数必须为
const
。
class Vector2 {
public:int x, y;Vector2(int a, int b) : x(a), y(b) {}friend std::ostream& operator<<(std::ostream& os, const Vector2& vec);
};std::ostream& operator<<(std::ostream& os, const Vector2& vec) {os << "(" << vec.x << ", " << vec.y << ")";return os;
}
4.3 前置/后置自增运算符 ++
- 前置
++
:返回当前对象的引用(T&
),不涉及 const。 - 后置
++
:返回临时对象(T
),通常需要 const 版本。
class Counter {
private:int value;
public:Counter(int v = 0) : value(v) {}// 前置 ++(非 const)Counter& operator++() {++value;return *this;}// 后置 ++(返回临时对象)Counter operator++(int) {Counter temp = *this;++value;return temp;}
};
五、常见错误与解决方案
5.1 错误:const 对象调用非 const 成员函数
const Array arr;
arr[3] = 100; // 编译错误:const 对象不能调用非 const 的 operator[]
解决方案:为运算符提供 const 版本。
5.2 错误:const 成员函数修改成员变量
void MyClass::print() const {data = 10; // 编译错误:const 成员函数不能修改成员变量
}
解决方案:移除修改成员变量的代码,或移除 const
修饰符(视需求而定)。
5.3 错误:赋值运算符重载为 const 成员函数
class String {
public:String& operator=(const String& other) const {// ... 逻辑错误:const 成员函数不能修改 this 指向的对象}
};
解决方案:移除 const
修饰符。
六、总结
6.1 核心要点
- const 成员函数是运算符重载的重要组成部分,用于支持 const 对象的操作。
- 运算符重载的 const 版本必须与非 const 版本的行为一致,但仅允许读取数据。
- 流输出运算符
<<
必须以友元函数实现,并且右操作数需为const
。 - 赋值运算符
=
不能是 const 成员函数,因为它会修改当前对象的状态。
6.2 最佳实践
- 始终为运算符提供 const 版本,以支持 const 对象。
- 返回值设计:const 版本返回
const T&
,非 const 版本返回T&
。 - 避免权限放大问题:const 对象不能调用非 const 成员函数。