运算符重载
内容简介
运算符重载允许自定义类型像内置类型一样使用运算符。例如让 Point + Point 表示坐标相加,让 std::cout << obj 输出对象内容。它的目标是提升表达力,不是炫技。
用法
可重载和不可重载
常见可重载运算符:
- 算术:
+ - * / % - 比较:
== != < > <= >= - 赋值:
= += -= *= /= - 下标:
[] - 函数调用:
() - 流输出:
<< - 自增自减:
++ -- - 内存:
new delete new[] delete[]
不可重载:
.、.*、::、sizeof、?:、#
算术运算符
class Point {
public:
Point(int x, int y) : x_(x), y_(y) {}
Point operator+(const Point& other) const
{
return Point(x_ + other.x_, y_ + other.y_);
}
private:
int x_ = 0;
int y_ = 0;
};比较运算符
class Point {
public:
bool operator==(const Point& other) const
{
return x_ == other.x_ && y_ == other.y_;
}
private:
int x_ = 0;
int y_ = 0;
};流输出运算符
class Point {
public:
Point(int x, int y) : x_(x), y_(y) {}
friend std::ostream& operator<<(std::ostream& os, const Point& point)
{
os << "(" << point.x_ << ", " << point.y_ << ")";
return os;
}
private:
int x_ = 0;
int y_ = 0;
};返回 std::ostream& 是为了支持链式输出:
std::cout << point << std::endl;下标运算符
class Buffer {
public:
int& operator[](std::size_t index)
{
return data_[index];
}
const int& operator[](std::size_t index) const
{
return data_[index];
}
private:
std::vector<int> data_;
};函数调用运算符
class GreaterThan {
public:
explicit GreaterThan(int base) : base_(base) {}
bool operator()(int value) const
{
return value > base_;
}
private:
int base_ = 0;
};自增运算符
class Counter {
public:
Counter& operator++()
{
++value_;
return *this;
}
Counter operator++(int)
{
Counter old = *this;
++(*this);
return old;
}
private:
int value_ = 0;
};最佳代码实践
- 运算符语义要符合直觉,
+就应该像加法,==就应该表示等价。 - 不修改对象的运算符成员函数加
const。 - 参数优先用
const T&,返回值按语义选择值或引用。 operator<<通常写成非成员函数,必要时声明为friend。- 实现了
operator==,通常也要考虑operator!=;C++20 后可用默认比较简化。 - 有资源所有权的类要遵守“三/五/零法则”,不要只重载赋值却忘记析构和拷贝构造。
错误用法
std::ostream operator<<(std::ostream os, const Point& point); // 错误:流对象不能按值拷贝Counter& operator++(int)
{
Counter old = *this;
++value_;
return old; // 错误:返回局部变量引用
}class Person {
public:
int* age = new int(18);
};
Person a;
Person b = a; // 错误风险:默认拷贝导致两个对象共享同一块堆内存注意事项
- 不要重载让读者意外的运算符,例如用
+表示保存文件。 - 后置
++通常比前置++多一次旧值保存,能用前置就用前置。 - 自定义
operator new/delete是高级用法,普通业务代码很少需要。 - 类型转换运算符容易造成隐式转换问题,通常配合
explicit。
学习路径
- 上一节:cpp异常处理
- 当前阶段:cpp进阶编程
- 下一节:cppOOP知识地图