运算符重载
引言
对于内置数据类型,编译器知道如何进行运算,这里我们以两数之和为例:
int a =10;
int b =20;
int c =a+b;//c=30
上面的加法运算是毫无疑问可以运行的,但倘若如下呢:
class num
{
public:
int m_a;
int m_b;
};
num n1;
n1.m_a=10;
n1.m_b=20;
num n2;
n2.m_a=15;
n2.m_b=25;
num n3;
n3=n1+n2;
这样编译器是无法解析并运行的,这里就运用到我们的运算符重载这个定义了
本篇文章将介绍几个重要的运算符重载以及他们的使用方法
1.加号运算符重载
在引言中,其实我们已经初步抽象了解到了加号运算符重载,但不正确,接下来我们来学习一下如何正确使用它来达到我们想要的目标,
1)通过成员函数重载
关键函数:
num operator+(num & p)
{
num temp;
temp.m_a=this->m_a+p.m_a;
temp.m_b=this->m_b+p.m_b;
return temp;
}
我们只需在类内声明这一个函数,就可以达到我们想要的目标,完整代码如下:
#include<iostream>
#include<string>
using namespace std;
class num
{
public:
int m_a;
int m_b;
num operator+(num & p)
{
num temp;
temp.m_a=this->m_a+p.m_a;
temp.m_b=this->m_b+p.m_b;
return temp;
}
};
void test_01()
{
num n1;
n1.m_a=10;
n1.m_b=20;
num n2;
n2.m_a=15;
n2.m_b=25;
num n3;
n3=n1+n2;
cout <<"n3a:"<<n3.m_a<<"\n"<<"n3b:"<<n3.m_b<<endl;
}
int main()
{
test_01();
return 0;
}
本质上n3=n1+n2;调用其实就是
n3=n1.operator(n2);
结果如下:

2)通过全局函数重载
将函数放在外面,再传入一个对象参数
class num
{
public:
int m_a;
int m_b;
};
num operator+(num & p1,num&p2)
{
num temp;
temp.m_a=p1.m_a+p2.m_a;
temp.m_b=p1.m_b+p2.m_b;
return temp;
}
这里的调用就是
n3=operator(n1,n2);
2.左移运算符重载
在我们想达到下面代码想展示的效果时,如果按照这个写法肯定会出错,这是一个不规则输出,那么这一节我们就来讲解左移运算符重载
class num { public: int m_a; int m_b; }; num n1; n1.m_a=10; n1.m_b=20; cout<<n1;
1)通过全局函数重载
关键函数
ostream& operator<<(ostream &cout,num & p)
{
cout<<"n1_a="<<p.m_a<<"n1_b="<<p.m_b<<endl;
return cout;
}
完整代码如下:
#include<iostream>
#include<string>
using namespace std;
class num
{
public:
int m_a;
int m_b;
};
ostream& operator<<(ostream &cout,num & p)
{
cout<<"n1_a="<<p.m_a<<"n1_b="<<p.m_b<<endl;
return cout;
}
void test_01()
{
num n1;
n1.m_a=10;
n1.m_b=20;
cout<<n1<<endl;
}
int main()
{
test_01();
return 0;
}
结果如下:

是不是还有疑问?——“为什么这个没有成员函数重载?”
我们先看看代码
class num { public: int m_a; int m_b; void operator<<(ostream& cout)//抽象化 { ... } };如果是这样,那么我们调用函数时需要
n1.operator<<(cout); 这样简化是 n1<<cout 看起来怪怪的,不规范 虽然有点绕,但是再多看看加号运算符重载可能会好理解一点
3.递增运算符重载
当我们想实现下面这个效果时,就需要运用到我们的递增运算符重载
class num { public: num() { m_a=10; } private: int m_a; };num n1; cout<<++n1<<endl;//11 cout<<n1++<<endl;//11 cout<<n1<<endl;//12
1).重载前置++运算符
关键函数
num & operator++()
{
m_a++;
return *this;
}
这里还需要用到前一节的左移运算符重载,完整代码展示
#include<iostream>
#include<string>
using namespace std;
class num
{
friend ostream& operator<<(ostream &cout,num & p);//友元
public:
num()
{
m_a=10;
}
num & operator++()
{
++m_a;
return *this;
}
private:
int m_a;
};
ostream& operator<<(ostream &cout,num & p)
{
cout<<p.m_a<<endl;
return cout;
}
void test_01()
{
num n1;
cout<<++(++n1)<<endl;
cout<<n1;
}
int main()
{
test_01();
return 0;
}
输出如下:

这时候你是否会有一个疑问
为什么重载函数要返回引用?——在我们调用++后,如果不引用,返回的将会是一个新对象,以上面代码为例
//引用之后 cout<<++(++n1)//12 cout<<n1//12 //不引用 cout<<++(++n1)//12 cout<<n1//11这是因为++n1之后返回的已经是一个新的对象,第二次++是对新对象的操作
2).重载后置++运算符
关键函数
num operator++(int)//占位参数,用于区分前置和后置
{
num temp=*this;
m_a++;
return temp;
}
这时候是不是肯定又有疑惑?——为什么这个又不用引用了啊!!
num& operator++() { m_a++; // 先自增 return *this; // 返回已经变的自己 }如果是这样,再返回的时候已经++过了,达不到先输出后加的效果
接下来让我形象的来解释一下:num operator++(int) { num temp = *this; // 🎭 给别人看的“原来的我” m_a++; // 🤫 偷偷地自己+1 return temp; // 🪞 送出伪装的旧我 }这段代码其实就是这个意思,我们用临时对象
复制出一个还没变化的自己,实际上自己偷偷+1,最后给别人展示临时对象(也就是变化之前的自己)
完整代码如下:
#include<iostream>
#include<string>
using namespace std;
class num
{
friend ostream& operator<<(ostream &cout,const num & p);
public:
num()
{
m_a=10;
}
num operator++(int)//占位参数,用于区分前置和后置
{
num temp=*this;
m_a++;
return temp;
}
private:
int m_a;
};
ostream& operator<<(ostream &cout,const num & p)
{
cout<<p.m_a<<endl;
return cout;
}
void test_01()
{
num n1;
cout<<n1++<<endl;
cout<<n1;
}
int main()
{
test_01();
return 0;
}
结果如下:

4.赋值运算符重载
当我们执行类似以下的赋值操作时:
class num { public: int m_a; //不用指针 num(int value = 0) { m_a = value; // 简单赋值 } }; num n1; num n2; n2 = n1;此时赋值、拷贝都不会有任何问题;
编译器生成的默认赋值运算符和拷贝构造函数都没问题;
假如我们的构造函数中使用了
new来动态分配内存:class num { public: int* m_a; num(int value) { m_a = new int(value); } ~num() { if (m_a != nullptr) { delete m_a; m_a = nullptr; } } };在上述类中,
m_a指向堆上的内存。当我们执行n2 = n1;时,n1.m_a的地址被浅拷贝到了n2.m_a,两个对象指向了同一块内存。这样会带来严重问题:
- 当
n1和n2被销毁时(比如出了作用域),它们都会调用析构函数;- 析构函数中都会执行
delete m_a;,导致对同一块内存重复释放(double free);- 此外,如果一个对象修改了
*m_a的值,也会影响另一个对象;- 这会造成 悬挂指针(dangling pointer) 或 重复释放 的问题。
接下来就介绍本小节的赋值运算符重载
关键函数
num& operator=(const num&p)
{
if(m_a!=NULL)
{
delete m_a;
m_a=NULL;
}
m_a=new int (*p.m_a);
return *this;
}
完整代码
#include<iostream>
#include<string>
using namespace std;
class num
{
public:
int * m_a;
num(int value)
{
m_a=new int(value);
}
~num()
{
if(m_a!=NULL)
{
delete m_a;
m_a=NULL;
}
}
num& operator=(const num&p)
{
if(m_a!=NULL)
{
delete m_a;
m_a=NULL;
}
m_a=new int (*p.m_a);
return *this;
}
};
void test_01()
{
num n1(10);
num n2(20);
num n3(32);
n3=n2=n1;
cout<<*n2.m_a<<endl;
}
int main()
{
test_01();
return 0;
}
输出结果如下:

5.关系函数重载
关键函数
bool operator==(const person &p)
{
if(p.m_age==this->m_age && p.m_name==this->m_name)
{
return true;
}
return false;
代码比较简单我直接展示完整函数
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
person(string name ,int age)
{
m_name=name;
m_age=age;
}
bool operator==(const person &p)
{
if(p.m_age==this->m_age && p.m_name==this->m_name)
{
return true;
}
return false;
}
string m_name;
int m_age;
};
void test_01()
{
person p1("张三",18);
person p2("李四",19);
if(p1==p2)
{
cout<<"equal"<<endl;
}
else
cout<<"not equal"<<endl;
}
int main()
{
test_01();
return 0;
}
结果如下:

6.函数调用运算符重载
关键函数
void operator()(string test)
{
cout<<test<<endl;
}
完整代码:
#include<iostream>
#include<string>
using namespace std;
class Myprint
{
public:
void operator()(string test)
{
cout<<test<<endl;
}
};
void test_01()
{
Myprint p1;
p1("我勒个骚刚");
}
int main()
{
test_01();
return 0;
}
直接函数也可以完成同样的效果
void test_02(string text) { cout<<text<<endl; } test_02("woleig");这么一看是不是函数重载使用起来非常像函数调用呢,所以我们称其位
仿函数,它是非常灵活的,没有固定的写法,比如下面的代码,我们实现一个加法class Myprint { public: int operator()(int a,int b) { return a+b; } }; void test_01() { Myprint p1; int num=p1(5,8); cout<<num<<endl; cout<<Myprint()(33,22)<<endl;//匿名对象调用 }调用之后输出结果就为12和55
那么我们就先介绍到这里,感谢你的阅读,希望这篇文章对你有帮助😊
243

被折叠的 条评论
为什么被折叠?



