C++中sizeof、?:、::、.、.*五个运算符完全禁止重载;=、[]、()、->只能作为类的非静态成员函数重载;

哪些运算符能重载、哪些不能
不是所有运算符都能重载:sizeof、?:(三目)、::(作用域)、.(成员访问)、.*(指针到成员)这五个完全禁止重载。其余大部分可以,但有隐含限制:比如 =、[]、()、-> 只能作为类的非静态成员函数重载,不能是全局函数;而 +、==、 这类则既可成员也可友元(推荐友元,尤其对流操作符)。
常见误用是试图重载 . 来实现“透明代理”,结果编译报错 error: 'operator.' must be a non-static member function —— 实际上 C++ 不允许这么做,得改用 -> 配合智能指针或代理类模式绕过。
为什么 operator 必须用友元函数
因为左操作数是 std::ostream&(如 std::cout),它不属于你的类,你没法给 std::ostream 添加成员函数。所以必须写成全局函数,再用 friend 打开类的私有访问权限:
class Vec {
int x_, y_;
public:
Vec(int x, int y) : x_(x), y_(y) {}
friend std::ostream& operator<<(std::ostream& os, const Vec& v) {
return os << "(" << v.x_ << ", " << v.y_ << ")";
}
};
漏掉 friend 会导致编译错误 error: 'x_' is private within this context;不加 const Vec& 引用参数,则临时对象无法绑定,出现 error: binding reference to a temporary。
立即学习“C++免费学习笔记(深入)”;
赋值运算符 operator= 的深拷贝陷阱
默认生成的 operator= 是浅拷贝,遇到含裸指针的类会引发双重释放或悬空指针。手动重载时必须做三件事:
- 自赋值检查:
if (this == &other) return *this; - 释放当前资源(如有)
- 深拷贝新资源,并返回
*this
更安全的做法是用“拷贝-交换”惯用法(copy-and-swap):
Vec& operator=(Vec other) { // 注意:传值,自动调用拷贝构造
swap(*this, other);
return *this;
}
friend void swap(Vec& a, Vec& b) noexcept {
using std::swap;
swap(a.x_, b.x_);
swap(a.y_, b.y_);
}
这样天然规避自赋值问题,也保证异常安全。但注意:如果类里有大对象(如 std::vector),传值成本高,此时仍建议传统写法 + 显式深拷贝。
前置 vs 后置递增:++a 和 a++ 的签名区别
前置递增(++a)返回引用,后置(a++)返回旧值,靠一个无用的 int 参数区分:
Vec& operator++() { // 前置
++x_; ++y_;
return *this;
}
Vec operator++(int) { // 后置:int 是占位符,不使用
Vec old = *this;
++(*this); // 复用前置逻辑
return old;
}
常见错误是把后置写成 Vec& operator++(int) —— 返回局部对象的引用,导致未定义行为;或者忘记参数 int,编译器会把它和前置重载冲突,报错 redefinition of 'operator++'。
真正难处理的是当类管理动态内存时,后置递增的拷贝成本可能很高,这时候得权衡是否禁用后置版本(删掉它,或只提供前置),避免用户无意中写出低效代码。











