content_views"
class="markdown_views prism-tomorrow-night">
一、运算符重载
1.运算符重载的使用
当我们实现一个日期类的时候c;我们有时候需要比较两个类的大小。我们可以写一个普通的函数去进行比较c;但是这样比较繁琐c;我们能否像内置类型那样只需要使用比较符号来进行判断呢?其实是可以的c;C++为什么支持内置类型的比较呢?这是因为内置类型的都是祖师爷知道的c;祖师爷知道他们如何进行比较c;所以将内置类型的比较方法早已写入库中c;而自定义类型祖师爷是不知道的c;所以需要我们自己来实现c;我们在实现的时候为了可以使用普通的比较符号c;我们可以使用运算符重载
<code class="prism language-cpp">class="token keyword">class class="token class-name">Date
class="token punctuation">{
class="token keyword">publicclass="token operator">:
class="token function">Dateclass="token punctuation">(class="token keyword">int year class="token operator">= class="token number">2023class="token punctuation">, class="token keyword">int month class="token operator">= class="token number">5class="token punctuation">, class="token keyword">int day class="token operator">= class="token number">10class="token punctuation">)
class="token punctuation">{
_year class="token operator">= yearclass="token punctuation">;
_month class="token operator">= monthclass="token punctuation">;
_day class="token operator">= dayclass="token punctuation">;
class="token punctuation">}
class="token comment">//private:
class="token keyword">int _yearclass="token punctuation">;
class="token keyword">int _monthclass="token punctuation">;
class="token keyword">int _dayclass="token punctuation">;
class="token punctuation">}class="token punctuation">;
class="token keyword">bool class="token keyword">operatorclass="token operator"><class="token punctuation">(class="token keyword">const Dateclass="token operator">& x1class="token punctuation">, class="token keyword">const Dateclass="token operator">& x2class="token punctuation">)
class="token punctuation">{
class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">< x2class="token punctuation">._yearclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">else class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">== x2class="token punctuation">._year class="token operator">&& x1class="token punctuation">._month class="token operator">< x2class="token punctuation">._monthclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">else class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">== x2class="token punctuation">._year class="token operator">&& x1class="token punctuation">._month class="token operator">== x2class="token punctuation">._month class="token operator">&& x1class="token punctuation">._day class="token operator">< x2class="token punctuation">._dayclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">return class="token boolean">falseclass="token punctuation">;
class="token punctuation">}
class="token keyword">int class="token function">mainclass="token punctuation">(class="token punctuation">)
class="token punctuation">{
Date class="token function">d1class="token punctuation">(class="token number">2023class="token punctuation">, class="token number">5class="token punctuation">, class="token number">11class="token punctuation">)class="token punctuation">;
Date class="token function">d2class="token punctuation">(class="token number">2023class="token punctuation">, class="token number">5class="token punctuation">, class="token number">12class="token punctuation">)class="token punctuation">;
cout class="token operator"><< class="token punctuation">(d1 class="token operator">< d2class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
cout class="token operator"><< class="token keyword">operatorclass="token operator"><class="token punctuation">(d1class="token punctuation">, d2class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
cout class="token operator"><< class="token punctuation">(d2 class="token operator">< d1class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
cout class="token operator"><< class="token keyword">operatorclass="token operator"><class="token punctuation">(d2class="token punctuation">, d1class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
class="token keyword">return class="token number">0class="token punctuation">;
class="token punctuation">}
code>
像上面代码中的d1<d2事实上就被转化为了operator<(d1,d2)c;我们可以从指令的角度来进行观察:可以发现其实是一样的
c="https://img-blog.csdnimg.cn/8e1e27aa476f45a19be99ebfedca1c50.png" alt="在这里插入图片描述" />
但是上面的代码仍然不够完善c;因为他是定义在类外的c;必须将私有成员变量给变为公有的c;如果想要使用私有的c;有两种方案:1.使用友元c;但是这样会破坏封装c;2.直接定义在类内c;这样的话我们需要注意一点的是c;在内里的变量是有一个this指针的c;所以我们的形参数其实是需要少一个的
<code class="prism language-cpp"> class="token keyword">bool class="token keyword">operatorclass="token operator"><class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
class="token punctuation">{
class="token keyword">if class="token punctuation">(_year class="token operator">< xclass="token punctuation">._yearclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">else class="token keyword">if class="token punctuation">(_year class="token operator">== xclass="token punctuation">._year class="token operator">&& _month class="token operator">< xclass="token punctuation">._monthclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">else class="token keyword">if class="token punctuation">(_year class="token operator">== xclass="token punctuation">._year class="token operator">&& _month class="token operator">== xclass="token punctuation">._month class="token operator">&& _day class="token operator">< xclass="token punctuation">._dayclass="token punctuation">)
class="token punctuation">{
class="token keyword">return class="token boolean">trueclass="token punctuation">;
class="token punctuation">}
class="token keyword">return class="token boolean">falseclass="token punctuation">;
class="token punctuation">}
code>
这时候他就是这样的等价了
c="https://img-blog.csdnimg.cn/56d0276f216a42dabc800aa06d12b1d5.png" alt="在这里插入图片描述" />
2.运算符重载的注意事项
ckquote>
C++为了增强代码的可读性引入了运算符重载c;运算符重载是具有特殊函数名的函数c;也具有其返回值类型c;函数名字以及参数列表c;其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
ckquote>
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符c;其含义不能改变c;例如:内置的整型+c;不 能改变其含义
- 作为类成员函数重载时c;其形参看起来比操作数数目少1c;因为成员函数的第一个参数为隐藏的this
- .* :: sizeof ?: . 注意以上5个运算符不能重载
- 操作符是几个操作数c;重载运算符函数就有几个参数
二、赋值运算符重载
1.复制拷贝与拷贝构造
首先我们需要搞清楚复制拷贝与拷贝构造的区别:
- 拷贝构造是使用一个已经存在的对象去初始化另外一个对象
- 复制拷贝是已经存在的两个对象进行的复制拷贝
c="https://img-blog.csdnimg.cn/57286ec540e9451e883c4c7818ad0107.png" alt="在这里插入图片描述" />
2.赋值运算符重载的格式
ckquote>
- 参数类型:const T&c;传递引用可以提高传参效率
- 返回值类型:T&c;返回引用可以提高返回的效率c;有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this :要复合连续赋值的含义
ckquote>
3.赋值运算符重载的实现
<code class="prism language-cpp"> Date class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
class="token punctuation">{
_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
class="token punctuation">}
code>
如上是复制运算符重载的实现c;这里我们需要注意的是c;我们需要传一个返回值c;这是为了满足连续赋值的场景。
但是还需要注意的是c;我们这里的是传值返回c;传值返回的过程会产生一个临时变量c;这个临时变量就会调用拷贝构造函数去构造这个临时变量。然后在将临时变量的数据交给变量
c="https://img-blog.csdnimg.cn/ddb53176d6c64ef2af919e8df184bb7c.png" alt="在这里插入图片描述" />
c="https://img-blog.csdnimg.cn/302a244bd85b442f89ba63b88b6a8fc8.png" alt="在这里插入图片描述" />
c="https://img-blog.csdnimg.cn/516cf7c3bf2b4818a5c6200c1a17bd0e.png" alt="在这里插入图片描述" />
但是这样的话代价太大了c;因为需要调用拷贝构造c;我们注意到*this出了这个赋值运算符重载函数还在c;所以我们可以使用传引用返回c;这样就减少了拷贝构造的代价了
<code class="prism language-cpp"> Dateclass="token operator">& class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
class="token punctuation">{
cout class="token operator"><< class="token string">"Date operator=(const Date& x)" class="token operator"><< endlclass="token punctuation">;
_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
class="token punctuation">}
code>
c="https://img-blog.csdnimg.cn/375501c085244b92a0965a97f2c5839b.png" alt="在这里插入图片描述" />有时候c;为了防止我们写出的d1=d1这样的代码c;我们可以进行这样的优化
<code class="prism language-cpp"> Dateclass="token operator">& class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
class="token punctuation">{
cout class="token operator"><< class="token string">"Date operator=(const Date& x)" class="token operator"><< endlclass="token punctuation">;
class="token keyword">if class="token punctuation">(class="token keyword">this class="token operator">!= class="token operator">&xclass="token punctuation">)
class="token punctuation">{
_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
class="token punctuation">}
class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
class="token punctuation">}
code>
或许可能会认为这里有点作用c;但不大c;只是起了优化的作用而已c;其实不是这样的c;这是因为日期类只涉及到浅拷贝c;如果涉及到深拷贝的话c;就会出现内存泄漏了。所以是非常有必要这样写的
4.赋值运算符重载的注意事项
- 赋值运算符只能重载成类的成员函数不能重载成全局函数
因为他是默认成员函数c;自己在类中不写c;编译器会自己生成c;就会产生冲突
- 用户没有显式实现时c;编译器会生成一个默认赋值运算符重载c;以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的c;而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值
默认生成的赋值运算符重载与拷贝构造的行为一样:
- 内置类型成员 --值拷贝/浅拷贝
- 自定义类型成员调用它的赋值重载
所以我们就可以看出来c;Date类和MyQueue类是不需要自己写赋值重载的c;而Stack是需要写的c;因为默认生成的是浅拷贝
本节内容就到这里了c;如果对你有帮助的话c;不要忘记点赞加收藏哦!!!