一.加号运算符重载
1.成员函数重载加号
#include<iostream>
using namespace std;
class Person{
public:
int age_A;
int age_B;
Person operator+(Person p1){//成员函数重载了+号
Person temp;
temp.age_A=0;
temp.age_B=0;
temp.age_A=this->age_A+p1.age_A;
temp.age_B=this->age_B+p1.age_B;
return temp;
}
};
int main(){
Person p1;
p1.age_A=10;
p1.age_B=100;
Person p2;
p2.age_A=10;
p2.age_B=100;
Person p3=p1+p2;//‘+’为重载之后的‘+’
cout<<p3.age_A<<endl;
cout<<p3.age_B<<endl;
}
将函数名命名为operator+,就可以对‘+’进行重载
2.全局函数重载加号
#include<iostream>
using namespace std;
class Person{
public:
int age_A;
int age_B;
};
Person operator+(Person p1,Person p2){
Person temp;
temp.age_A=0;
temp.age_B=0;
temp.age_A=p1.age_A+p2.age_A;
temp.age_B=p1.age_B+p1.age_B;
return temp;
}
int main(){
Person p1;
p1.age_A=10;
p1.age_B=100;
Person p2;
p2.age_A=10;
p2.age_B=100;
Person p3=p1+p2;
cout<<p3.age_A<<endl;
cout<<p3.age_B<<endl;
}
原理和成员函数基本相同,返回值为类型,区别只是参数变成了两个类
二.左移运算符重载
左移运算符和加号运算符不同,如果在成员函数中进行重载,那么不能达到预期效果,这里通常只能在全局函数中重载
#include<iostream>
using namespace std;
class Person{
public:
int m_A;
int m_B;
};
ostream& operator<<(ostream& t,Person& p){
t<<"m_A="<<p.m_A<<" m_B="<<p.m_B<<endl;
return t;
}
int main(){
Person p1;
p1.m_A=10;
p1.m_B=10;
cout<<p1<<endl;
Person p2;
p2.m_A=20;
p2.m_B=20;
cout<<p2<<endl;
}
1.注意看operator<<函数,将ostream类型进行改写,这里一定要是引用,否则后续在使用时会报错。
2.在最后返回了一个ostream的引用,是为了支持链式操作。(因为当我们使用 ostream
对象进行输出时,通常会进行链式操作,比如 cout << "Hello" << endl;
。这是因为 <<
运算符会返回其左操作数的引用,以便我们可以继续在其上执行输出操作。如果 ostream
对象作为值传递,那么每次 <<
运算符调用都会生成一个新的 ostream
对象,导致链式操作失效。)
三.递增运算符重载
#include<iostream>
using namespace std;
class MyInt{
//设置为友元,访问私有成员变量
friend ostream& operator<<(ostream& cout,const MyInt &i);
public:
MyInt(){
num=0;
}
//前置自增运算符
MyInt& operator++()
{
//先进行++运算
num++;
//再将自身返回
return *this;
}
// 后置自增运算符
// int代表占位参数,可以用于区分前置和后置自增
MyInt operator++(int)
{
MyInt temp = *this;
num++;
return temp;
}
private:
int num;
};
ostream& operator<<(ostream& cout,const MyInt &i){
cout << i.num;
return cout;
}
int main(){
MyInt myint;
cout << ++(++myint) <<endl;
cout << myint++ << endl;
cout << myint <<endl;
}
注意看前置运算和后置运算的区别
前置递增时最后返回的是一个类的引用,这是因为前置递增一般可以进行链式操作。而后置运算符返回的是对象递增前的值的副本,因此不能进行链式操作。
链式操作详解:
在表达式 cout << a << b;
中,编译器会从左向右逐个解析表达式,并根据运算符的优先级和结合性确定表达式的含义。在这个例子中,<<
是左结合的,因此编译器会首先解析 cout << a
这个子表达式。
因此,编译器首先识别的是 cout << a
这一部分。它会查找一个与左操作数 cout
和右操作数 a
匹配的重载运算符 <<
。由于标准库中已经定义了 cout
的 <<
运算符,它将 a
的值输出到 cout
中,并返回一个 ostream&
对象,表示输出流本身。
然后,编译器将这个返回的 ostream&
对象视为整个表达式的左操作数,接着解析 << b
部分。同样地,编译器会查找与左操作数匹配的重载运算符 <<
,并将 b
的值插入到输出流中。
四.赋值运算符重载
#include<iostream>
using namespace std;
class Person{
public:
Person& operator=(Person &p){
//如果之前m_Age已经有数据了,那么要先销毁
if(m_Age!=nullptr){
delete m_Age;
m_Age=nullptr;
}
m_Age=new int(*p.m_Age);//这里进行深拷贝操作,防止在构析函数中销毁堆区中的数据时进行重复销毁操作
return *this;
}
~Person()//析构函数释放之前new在堆区的数据
{
if(m_Age!=nullptr)
{
delete m_Age;
m_Age=nullptr;
}
}
Person(int age){
m_Age=new int(age);
}
int* m_Age;
};
int main(){
Person p1(18);
Person p2(20);
Person p3(30);
p1=p2=p3;
cout<< *(p1.m_Age)<<endl;
}
注意点:
在赋值重载函数中,对m_Age这种指针类型的赋值采用了深拷贝的方法,这是因为每一次赋值如果采用浅拷贝的话,最后在析构函数中就会重复删除已经为空的m_Age,导致编译器出错
五.关系运算符重载
重载==号以及重载!=号
#include<iostream>
using namespace std;
class Person{
public:
Person(string name,int m_Age){
this->name=name;
this->m_Age=m_Age;
}
//重载==运算符
bool operator==(Person &p){
if(this->m_Age==p.m_Age&&this->name==p.name){
return true;
}else{
return false;
}
}
//重载!=运算符
bool operator!=(Person &p){
if(this->m_Age==p.m_Age&&this->name==p.name){
return false;
}else{
return true;
}
}
string name;
int m_Age;
};
void test(){
Person p1("黎明",50);
Person p2("刘德华",55);
Person p3("黎明",50);
if(p1==p3){
cout<<"p1和p3是一样的"<<endl;
}
if(p1!=p2){
cout<<"p1和p2是不一样的"<<endl;
}else{
cout<<"p1和p2是一样的"<<endl;
}
}
int main(){
test();
}
这个非常简单,只需要返回一个bool类型的值就可以
五.函数调用运算符重载
#include<iostream>
using namespace std;
class Myprint{
public:
//函数调用运算符重载
void operator()(string content){
cout<<content<<endl;
}
};
void test(){
Myprint print;
print("ppthat1");
}
void print(string s){
cout<<s<<endl;
}
int main(){
test();
print("nihaow");
}
注意看,这个重载的()操作符由于非常像一个函数,因此称为仿函数。仿函数没有固定的写法,因此非常灵活