运算符重载是接口设计而非语法糖,应使类行为如内置类型般自然;只重载有直观语义的运算符,优先==、!=、、+、-、*、[]、()等,谨慎=、&&、||、,,禁用?:、::、.等;成员/非成员选择依转换需求与对称性而定,坚持无副作用、行为一致、显式控制隐式转换。

运算符重载不是语法糖,而是接口设计——它该让类的行为“像内置类型一样自然”,而不是炫技或绕过类型系统。用错地方反而破坏可读性、引发隐式转换陷阱,甚至导致二义性编译错误。
只重载有直观语义的运算符
比如 operator+ 应表示“可交换的、无副作用的组合”,operator== 应满足自反、对称、传递;而 operator* 用于矩阵类很自然,但给一个日志类重载 operator* 就令人困惑。
- 优先重载:==, !=, , +, -, *, [], (), ->, ++, –(尤其是前缀/后缀语义要分清)
- 谨慎重载:=, &&, ||, ,(逗号),new/delete —— 它们自带特殊语义或求值顺序保证,改写易出错
- 避免重载:?:, ::, ., sizeof, typeid, static_cast 等——语言禁止或不建议
按惯例选择成员 or 非成员实现
核心原则:左侧操作数需隐式转换时,必须用非成员函数(常为友元);涉及对称性或需要访问私有成员时,再考虑友元。
- operator== / operator:通常非成员,支持左操作数是其他类型(如
std::String s; if (s == "hello")) - operator+= / operator-=:推荐成员函数(修改自身,且左侧必为本类对象)
- operator+ / operator-:习惯上用非成员,内部调用 operator+=(即
return T(lhs) += rhs;),避免重复逻辑 - operator[] / operator()-> / operator++:必须是成员(只有成员能定义这些)
保持行为一致与无副作用
用户看到 a + b,默认期待它不修改 a 或 b,返回新对象;看到 a += b,才预期 a 被修改。打破这个直觉就是bug温床。
立即学习“C++免费学习笔记(深入)”;
- 二元中缀运算符(+, -, ==)应是 const、不修改任何参数
- 复合赋值(+=, -=)应返回 *this 的引用,支持链式调用(
a += b += c;) - 前缀 ++/– 返回引用,后缀版本返回旧值(通常用 int 哑参区分),并注意效率:后缀应避免拷贝大对象,可先保存再调用前缀
- 不要在 operator== 里抛异常、做IO、或触发复杂计算——它该是轻量、确定、快速的
显式控制隐式转换,防止意外调用
带单参数构造函数(或 conversion operator)容易引发静默转换,让重载运算符被误触发。例如:
class String { public: String(const char*); }; String s = “abc”; if (s == “def”) // OK —— 但若还有 String(int),”s == 42″ 就可能意外调用!
解决方案:
- 把单参构造函数声明为 explicit(c++11起默认推荐)
- conversion operator 也加 explicit(C++11),如
explicit operator bool() const; - 对关键运算符(如 ==),可用模板 + enable_if 限制右操作数类型,或提供仅接受特定类型的重载
基本上就这些。运算符重载不是越多越好,而是越少、越准、越符合直觉越好。宁可多写几个命名函数(如 multiply_by()),也不要让 operator* 变成“执行某种业务逻辑”的黑盒。