操作符重载的作用
使我们可以像基本数据类型那样用运算符;
仿函数是建立在操作符重载的基础上的;
stl中容器的算法也是建立在操作符重载的基础上的;
首先我个人认为去写操作符重载要掌握的最重要的原则是:你写的这个重载操作符要传入几个参数。
其原则是看这个操作符重载是不是当前类的成员函数,如果是就只需要传一个参数(隐含参数:左操作数 (a) 通过 this指针传递);如果是类外或者友元等非成员函数那需要传两个参数
下面写几个案例
举例说明
下面是不能两中方式一起写的,这会造成编译错误(二义性错误)
对于 +这类不修改左操作数、通常返回新对象的对称性运算符,更推荐使用非成员函数(友元)形式。这样做还有一个好处:如果你希望支持 (整数 + Complex对象)这样的表达式,非成员函数是必须的,因为整数不是你的类类型,不能作为成员函数的调用者。
如果 operator+是成员函数,5 + c1将无法编译。(c1是Complex的对象)
class Complex{
public:
Complex operator+(const Complex& rightOperand) const
{
return Complex(real+rightOperand.real,imag+rightOperand.imag);
}
friend Complex operator+(const Complex& leftOperand,const Complex& rightOperand);
private:
double real,imag;
};
Complex operator+(const Complex& leftOperand,const Complex& rightOperand)
{
return Complex(leftOperand.real+rightOperand.real,leftOperand.imag+rightOperand.imag);
}
1. 算术运算符与复合赋值运算符 (+, +=)
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 复合赋值运算符 +=,通常作为成员函数(修改左操作数)
Complex& operator+=(const Complex& rhs) {
real += rhs.real;
imag += rhs.imag;
return *this; // 返回引用以支持链式赋值 (a += b) += c
}
// 提供友元函数形式,以便支持左操作数为非本类对象的情况
friend Complex operator+(const Complex& lhs, const Complex& rhs);
};
// 算术运算符 +,作为非成员友元函数(不修改操作数,生成新对象)
Complex operator+(const Complex& lhs, const Complex& rhs) {
return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
// 也可以利用已实现的 += 来提升效率:Complex result(lhs); return result += rhs;
}
int main() {
Complex a(1.0, 2.0), b(3.0, 4.0);
Complex c = a + b; // 调用 operator+(a, b)
a += b; // 调用 a.operator+=(b)
return 0;
}
2. 关系运算符 (==, !=)
class Complex {
// ... 其他成员同上
public:
// 关系运算符 ==,通常重载为友元函数
friend bool operator==(const Complex& lhs, const Complex& rhs) {
return (lhs.real == rhs.real) && (lhs.imag == rhs.imag);
}
// 关系运算符 !=,通常利用 == 来实现
friend bool operator!=(const Complex& lhs, const Complex& rhs) {
return !(lhs == rhs);
}
};
3. 输入与输出运算符 (>>, <<)
class Complex {
// ... 其他成员同上
public:
// 输入输出运算符必须重载为非成员函数
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os; // 返回流引用以支持链式操作 cout << a << b;
}
friend std::istream& operator>>(std::istream& is, Complex& c) {
is >> c.real >> c.imag;
return is; // 返回流引用以支持链式操作 cin >> a >> b;
}
};
4. 下标运算符 ([])
下标运算符 []必须重载为类的成员函数。为了同时支持常量对象和非常量对象的使用,通常需要提供两个版本。
class MyArray {
private:
int data[100];
size_t size;
public:
MyArray(size_t s = 100) : size(s) {}
// 非常量版本:允许通过下标修改数组元素
int& operator[](size_t index) {
if (index >= size) throw std::out_of_range("Index out of range");
return data[index];
}
// 常量版本:用于常量对象,只允许读取,返回 const 引用
const int& operator[](size_t index) const {
if (index >= size) throw std::out_of_range("Index out of range");
return data[index];
}
};
int main() {
MyArray arr;
arr[10] = 42; // 调用非常量版本,可以赋值
std::cout << arr[10]; // 调用非常量版本,可以读取
const MyArray const_arr;
// const_arr[10] = 50; // 错误!调用常量版本,不能赋值
std::cout << const_arr[10]; // 正确,调用常量版本,可以读取
return 0;
}
5. 递增与递减运算符 (++, --)
递增和递减运算符有前置和后置两种形式,需要通过参数来区分。
class Counter {
private:
int count;
public:
Counter(int c = 0) : count(c) {}
// 前置 ++:无参数,返回递增后的引用
Counter& operator++() {
++count;
return *this;
}
// 后置 ++:int 参数仅用于区分,无实际意义,返回递增前的副本(值)
Counter operator++(int) {
Counter temp = *this; // 保存原值
++(*this); // 利用前置 ++ 实现递增
return temp; // 返回原值
}
// 前置 -- 和后置 -- 类似
Counter& operator--() {
--count;
return *this;
}
Counter operator--(int) {
Counter temp = *this;
--(*this);
return temp;
}
};
stl容器算法 (自定义排序规则)
使用重载的 < 运算符进行排序
#include <algorithm>
#include <vector>
#include <string>
struct Person
{
std::string name;
int age;
bool operator<(const Person& other) const
{
return age<other.age;
}
};
int main()
{
std::vector<Person> people = {{"Alice", 25}, {"Bob", 20}, {"Charlie", 30}};
std::sort(people.begin(),people.end());
return 0;
}
使用仿函数
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
std::vector<int> numbers={3,1,4,1,5,9,2,6};
std::sort(numbers.begin(),numbers.end(),std::greator<int>());
struct LengthCompare
{
bool operator()(const std::string& a,const std::string& b) const
{
return a.length()<b.length();
}
}
std::vector<std::string> words={"apple","a","banana","cat"};
std::sort(words.begin(),words.end(),LengthCompare());
——
条件查找和计数 也常自定义逻辑
同样可以使用类成员函数重载操作符,也可以使用自定义仿函数
class GreaterThan
{
public:
GreaterThan(int threshold):threshold_(threshold) {}
bool operator()(int x) const {return x>threshold_;}
private:
int threshold_;
};
int main()
{
std::vector<int> scores={85,92,76,60,99,45};
//统计满足条件的数量有多少个(其实用lamada表达式显得逻辑更清晰,但用仿函数增加了复用性)
int count=std::count_if(scores.begin(),scores.end(),GreaterThan(80));
std::cout<<"大于80分的数量: "<<count<<std::endl;
//使用find_if 在范围内查找第一个满足特定条件的元素 返回的是迭代器
auto it_25=std::find_if(socres.begin(),socres.end(),[](int n){return n==25;}
if(it_25!=scores.end())
{
std::cout << "第一个等于25的元素: " << *it_25 << ",位于索引 " << (it_25 - scores.begin()) << std::endl; // 输出: 第一个等于25的元素: 25,位于索引 1
}
return 0;
}
变换元素
#include <algorithm>
#include <vector>
#include <iostream>
// 自定义仿函数:将元素乘以2
class MultiplyByTwo {
public:
int operator()(int x) const { return x * 2; }
};
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int> result(vec.size());
std::transform(vec.begin(), vec.end(), result.begin(), MultiplyByTwo());
// result = {2, 4, 6, 8, 10} [1,3](@ref)
return 0;
}