运算符重载
(Operator Overloading)允许为自定义类型(如类、结构体)赋予类似内置类型的运算符行为,使对象之间可以使用+、-、==、<<等运算符进行操作,提升代码的可读性和易用性。
1. 基本语法
• 通过operator
关键字重载运算符。
• 可以作为成员函数
或全局函数
实现。
示例(成员函数):
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
Point operator+(const Point& other) const {
return Point(x + other.x, y + other.y);
}
};
2. 加号运算符重载
成员函数方式:适用于a + b,其中a为当前对象,b为参数
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
// 加号运算符重载
Point operator+(const Point& other) const {
return Point(x + other.x, y + other.y);
}
};
int main() {
Point p1(1, 2), p2(3, 4);
Point p3 = p1 + p2; // 调用operator+
std::cout << p3.x << ", " << p3.y << std::endl; // 输出4, 6
return 0;
}
全局函数方式:适用于需要支持a + b和b + a等更灵活的场景(如左操作数不是类对象)。
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
Point operator+(const Point& a, const Point& b) {
return Point(a.x + b.x, a.y + b.y);
}
注意事项
• 推荐参数为const引用,返回新对象。
• 不建议修改参与运算的原对象(保持加法的“无副作用”)。
• 可重载为成员函数或全局函数,具体选择视需求而定。
总结:
加号运算符重载让自定义类型支持直观的“加法”操作,提升代码可读性和易用性。
3. 左移运算符重载
左移运算符重载(operator<<)常用于自定义类型的输出,使对象可以直接用std::cout << obj的方式打印内容。通常写成全局函数
,并返回ostream&
以支持链式输出
。
#include <iostream>
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
// 左移运算符重载,必须为全局函数(或友元)
std::ostream& operator<<(std::ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
int main() {
Point p(3, 4);
std::cout << p << std::endl; // 输出:(3, 4)
return 0;
}
说明与注意事项:
• 第一个参数为std::ostream&,第二个参数为自定义类型的const引用。
• 返回ostream&,支持链式输出(如std::cout << p1 << p2;)。
• 通常声明为友元函数以访问私有成员:
friend std::ostream& operator<<(std::ostream&, const Point&);
4. 递增运算符
递增运算符重载(operator++)允许自定义类型支持前置
和后置
自增(如++obj和obj++)。这两种写法的重载方式略有不同。
前置递增运算符重载:
• 语法:Type& operator++();
• 用于++obj,返回自增后的对象引用。
示例:
class Counter {
int value;
public:
Counter(int v = 0) : value(v) {}
// 前置++
Counter& operator++() {
++value;
return *this;
}
int get() const { return value; }
};
后置递增运算符重载:
• 语法:Type operator++(int);(int是占位参数,用于区分前置和后置)
• 用于obj++,返回自增前的对象副本。
示例:
class Counter {
int value;
public:
Counter(int v = 0) : value(v) {}
// 后置++
Counter operator++(int) {
Counter temp = *this;
++value;
return temp;
}
int get() const { return value; }
};
调用:
int main() {
Counter c(5);
++c; // 前置,c变为6
c++; // 后置,c变为7
std::cout << c.get() << std::endl; // 输出7
return 0;
}
注意事项
• 前置返回引用
,后置返回副本
。
• 推荐前置
效率更高,后置因需保存临时副本略慢。
• 可根据需要只重载一种或两种。
5. 赋值运算符
赋值运算符重载(operator=)允许自定义类型支持对象间的赋值操作(如a = b;)。正确实现赋值运算符对于资源管理(如动态内存)尤为重要。
基本语法:
class Demo {
public:
Demo& operator=(const Demo& other) {
if (this != &other) { // 防止自赋值
// 释放旧资源(如有)
// 复制other的数据到当前对象
}
return *this; // 支持链式赋值
}
};
典型实现:
class MyString {
char* data;
public:
MyString(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
~MyString() { delete[] data; }
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data; // 释放旧内存
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
};
注意事项:
• 防止自赋值
:if (this != &other),避免自我释放导致数据丢失。
• 释放旧资源
:先释放当前对象持有的资源,防止内存泄漏。
• 深拷贝
:如有指针成员,需分配新内存并复制内容。
• 返回自身引用
:return *this;,支持链式赋值(如a = b = c;)。
6. 关系运算符重载
关系运算符重载允许自定义类型支持比较操作(如==、!=、<、>、<=、>=),使对象之间可以像内置类型一样进行比较。
常见关系运算符重载:
• operator==:等于
• operator!=:不等于
• operator< :小于
• operator> :大于
• operator<=:小于等于
• operator>=:大于等于
示例
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
// 等于运算符重载
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
// 小于运算符重载(按字典序)
bool operator<(const Point& other) const {
return (x < other.x) || (x == other.x && y < other.y);
}
};
int main() {
Point p1(1, 2), p2(1, 2), p3(2, 3);
std::cout << std::boolalpha;
std::cout << (p1 == p2) << std::endl; // true
std::cout << (p1 < p3) << std::endl; // true
return 0;
}
注意事项:
• 推荐参数为const引用,成员函数加const保证不修改对象。
• 只需重载==和<,其余可用标准库std::rel_ops自动推导(或手动实现)。
• 关系运算符重载应符合直观语义,避免误用。
7. 函数调用运算符重载
函数调用运算符重载(operator())允许对象像函数一样被调用
,这种对象称为“仿函数
”或“函数对象
”。
函数调用运算符重载(operator())
• 通过重载operator(),可以让对象像函数一样使用,常用于自定义算法、回调、STL
等场景。
示例:
class Adder {
int base;
public:
Adder(int b) : base(b) {}
int operator()(int x) const {
return base + x;
}
};
int main() {
Adder add5(5);
std::cout << add5(10) << std::endl; // 输出15
return 0;
}
• 可以有多个参数,也可以重载多次实现不同参数列表。
• 支持捕获状态(如成员变量),比普通函数更灵活。
8. 其他运算符重载
• 下标运算符重载(operator[]):使对象支持数组下标访问
示例:
class Array {
int data[10];
public:
int& operator[](int idx) { return data[idx]; }
};
• 箭头运算符重载(operator->):使对象像指针一样访问成员。
class Ptr {
Demo* p;
public:
Demo* operator->() { return p; }
};
• 类型转换运算符重载(operator 类型):实现对象到其他类型的隐式或显式转换。
class Demo {
int value;
public:
operator int() const { return value; }
};
9. 注意事项
• 运算符重载应符合直观语义,避免滥用。
• 某些运算符(如[]、()、->、=)必须为成员函数。
• 运算符重载不能改变运算符的优先级和结合性。