=、+=、-=、*=、/=、[]、()、->、++(后置)、--(后置)、new、delete 这些运算符必须为成员函数,因语法或标准强制要求;+、== 等推荐非成员以支持对称性和隐式转换。

绝大多数二元运算符应该优先写成非成员函数(通常是友元),一元运算符和赋值类运算符(=、+= 等)必须用成员函数。
哪些运算符强制要求成员函数
以下运算符的语义或语言规则决定了它们只能作为成员函数实现:
-
=、+=、-=、*=、/=等复合赋值运算符:C++ 标准规定只有成员函数版本参与重载解析 -
[]、()、->、++(后置)、--(后置):语法上绑定到左操作数对象,隐含this上下文 -
new和delete:必须是静态成员函数
如果你尝试用非成员函数重载 +=,编译器会直接报错:error: 'operator+=' must be a non-static member function。
为什么 +、==、&& 这类运算符推荐非成员函数
核心原因是**对称性**和**隐式类型转换支持**:
立即学习“C++免费学习笔记(深入)”;
- 表达式
a + 5和5 + a都应合法(假设a是自定义类型),但成员函数版本a.operator+(5)无法处理5.operator+(a)(内置整数没有这个成员) - 非成员函数
operator+(const MyInt&, int)和operator+(int, const MyInt&)可分别定义,或靠一个const MyInt&参数 + 构造函数隐式转换来统一支持两边的整数 -
&&尤其典型:它有短路求值语义,而 C++ 不允许重载后保留短路;一旦你重载了operator&&,它就变成普通函数调用,失去短路行为。所以实践中几乎从不重载&&和||—— 如果真要重载,也必须是非成员函数,否则连obj && true这种基本形式都可能因隐式转换失败而编译不过
友元声明不是必须的,但通常需要
非成员函数默认不能访问类的私有成员。是否加 friend 取决于你如何设计接口:
- 如果类提供了公有的
get_real()、get_imag()等只读访问函数,非成员operator+可以只靠这些接口实现,无需friend - 如果想直接读写
real、imag成员,就必须在类内声明friend operator+;否则编译报错:error: 'real' is a private member of 'Complex' - 注意:加
friend不破坏封装性,它只是把访问权限明确授予特定函数,而非开放给所有外部代码
容易忽略的关键点
两个看似等价的重载版本同时存在会导致编译错误,而且错误信息往往不直观:
- 比如你既写了成员函数
Complex::operator+(const Complex&),又写了非成员operator+(const Complex&, const Complex&),编译器会报ambiguous overload—— 它无法决定选哪个 -
const修饰位置影响很大:operator+() const表示不修改当前对象,这是常规做法;但返回值如果是Complex&(非常量引用),就会和大多数使用场景冲突,因为临时对象不能绑定到非常量引用 - 重载
&&或||后,逻辑语义已变:它不再短路,且左右操作数都会被求值。除非你有非常特殊的 DSL 需求,否则应避免重载这两个运算符


















