重载 operator+ 应返回值类型 T 而非引用,避免返回局部对象引用导致野指针;左操作数为非当前类类型时必须用全局函数(常加 friend),因成员函数隐含 this 占据左侧位置。

重载 operator+ 时别返回局部对象的引用
返回 const T& 或 T& 是常见错误源头——加法结果是临时对象,栈上构造完就销毁,返回引用等于返回野指针。
- 正确做法:返回值类型必须是
T(值语义),让编译器决定是否启用 RVO 或移动构造 - 如果真想避免拷贝,确保类实现了移动构造函数(
T(T&&)),且编译器支持 C++11+ - 别为了“性能”强行返回
const T&,Clang/GCC 在 -O2 下会警告reference to stack memory
// ❌ 危险写法
const Vec2& operator+(const Vec2& a, const Vec2& b) {
Vec2 tmp(a.x + b.x, a.y + b.y);
return tmp; // tmp 离开作用域即析构
}
<p>// ✅ 安全写法
Vec2 operator+(const Vec2& a, const Vec2& b) {
return Vec2(a.x + b.x, a.y + b.y); // 返回值,由调用方接收
}成员函数 vs 全局函数:什么时候必须用全局重载
当运算符左操作数不是当前类类型(比如 int + MyString),成员函数无法接管——因为隐式 this 指针固定占了左边位置。
- 需要左操作数为内置类型或其它类时,必须定义非成员函数(常配合
friend访问私有成员) operator 和 <code>operator>>几乎总是全局函数,否则没法写std::cout- 二元运算符如
==、!=推荐全局实现,保持对称性;+=、-=这类复合赋值则应作为成员函数
// ✅ 支持 int + MyString
MyString operator+(int n, const MyString& s) {
return MyString(std::to_string(n)) + s;
}
<p>// ❌ 成员函数做不到这个调用形式
// s.operator+(n) → 是 MyString + int,不是 int + MyString重载 operator[] 要提供 const 和非 const 版本
否则 const 对象调用 [] 会编译失败,或者被迫返回可修改的引用,破坏 const 正确性。
- 非 const 版本返回
T&,支持赋值:arr[0] = 42; - const 版本返回
const T&,只读访问:const auto& x = arr[0]; - 两个版本不能仅靠返回值不同重载(C++ 不允许),必须用
const限定符区分成员函数
class Array {
std::vector<int> data_;
public:
int& operator[](size_t i) { return data_[i]; }
const int& operator[](size_t i) const { return data_[i]; }
};自增/自减运算符的后置版本必须带 int 形参
这是语法硬性约定,不是命名习惯。漏掉 int 参数会导致编译器把后置当成前置,行为错乱。
立即学习“C++免费学习笔记(深入)”;
- 前置(
++x)返回T&,直接改原对象并返回引用 - 后置(
x++)返回T(值),需先保存副本再自增,形参int是唯一用来区分重载的标记 - 别试图用默认参数或重命名绕过,编译器不认
// ✅ 正确签名
T& operator++() { /* 前置 */ }
T operator++(int) { /* 后置,int 是占位符 */ }
<p>// ❌ 编译不过
T operator++() { /<em> 这会被当成前置,和上面冲突 </em>/ }重载运算符真正难的不是语法,是判断「该不该重载」——比如 operator* 对矩阵有意义,对日志类就不该存在;一旦加了,用户就会按直觉用,而直觉背后全是契约。










