C++ 运算符重载与操作符重载

目录

运算符重载

运算符重载的特性

其他运算符重载的实现

默认成员函数——赋值运算符重载

默认成员函数——取地址操作符重载

const成员

附录


运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型函数名字以及参数列表

运算符重载实际上就如同函数重载,使操作符拥有新的功能。

结构:

返回值类型 operator操作符(参数列表)

例如

bool operator==(Date d1,Date d2);

定义一个Date类

class Date
{
public://构造函数Date(int year = 0, int month = 0, int day = 0){//判断日期是否合法//GetMonthDay()获取这个月的天数if (month > 0 && month < 13 &&(day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}}
private:int _year;//年int _month;//月int _day;//日
};

我们都知道==是用来比较的运算符,Date类对象进行比较该怎么比较呢?我们可以规定,如果两个对象的年、月、日都相当则两个对象相等,返回true

错误示例

class Date
{
public://构造函数Date(int year = 0, int month = 0, int day = 0){if (month > 0 && month < 13&& (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}}bool operator==(Date d1, Date d2){return (d1._year == d2._year) && (d1._month == d2._month) && (d1._day == d2._day);}
private:int _year;int _month;int _day;};

二元运算符的重载函数的参数有两个,规定第一个参数左操作数第二个参数右操作数

还记得成员函数有什么特性吗?成员函数有一个自带的参数this,类型为类类型。因为我们不可能将this指针删掉,所以只能省略第一个参数

为减少拷贝引起的消耗,尽量使用引用的方式传参

正确的做法

class Date
{
public://...bool operator==(const Date& d)//若不改变形参最好用const修饰{return (_year == d._year) && (_month == d._month) && (_day == d._day);}//...	
};

运算符重载的特性

运算符重载有如下特性:

  • 重载操作符必须有一个类类型参数;
  • 不能通过连接其他符号来创建新的操作符:比如operator@、operator?等;
  • 用于内置类型的运算符,其含义不能改变,例如:int类型的+,不能改变其含义;
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;
  • .* :: sizeof ?: .注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

其他运算符重载的实现

有了上述的==作为示例,我们还可以实现< > <= >= + - ++ --等一系列操作符的重载。

<    >     <=    >=      != 重载

class Date
{
public://构造函数//...bool operator==(const Date& d){return (_year == d._year) && (_month == d._month) && (_day == d._day);}bool operator<(const Date& d) {return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);}bool operator<=(const Date& d) {//函数的复用return *this < d || *this == d;}bool operator>(const Date& d) {//函数的复用return !(*this <= d);}bool operator>=(const Date& d) {//函数的复用return !(*this < d);}bool operator!=(const Date& d) {//函数的复用return !(*this == d);}
//...
};

+=   -=     +     -

注意:下列四个运算符的右操作数都为天数

class Date
{
public://...//获取当月的天数int GetMonthDay(int year, int month) {assert(month > 0 && month < 13);int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//判断是否是闰年的二月if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return monthArray[month];}}//+= 返回自身的引用,减少拷贝Date& operator+=(int day){//判断是否加了负数if (day < 0){//复用*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month));{_day -= GetMonthDay(_year, _month);//进位_month++;if (_month == 13){_year++;_month = 1;}}return *this;}//-= 返回自身的引用,减少拷贝Date& operator-=(int day){//判断是否减了一个负数if (day < 0){//复用*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;}Date operator+(int day) {//拷贝构造//因为加不改变自身的值,所以创建临时对象Date tmp(*this);//复用tmp += day;return tmp;}Date operator-(int day){Date tmp(*this);tmp -= day;return tmp;}
//...
};

前置++ 与 后置++ 重载

前置++和后置++都是一元运算符,为了让前置++与后置++能形成正确重载,C++规定:

  • 后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
    自动传递
class Date
{
public://...//前置++Date& operator++(){*this += 1;return *this;}//后置++// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,// 故需在实现时需要先将this保存一份,然后给this + 1// 而temp是临时对象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date tmp(*this);*this += 1;return tmp;}//前置--Date& operator--(){*this -= 1;return *this;}//后置--Date operator--(int){Date tmp(*this);*this -= 1;return tmp;}
//...
};

日期 - 日期的实现

日期+日期没有意义,但是日期-日期有意义,日期-日期代表相距多少天

class Date
{
//...int operator-(const Date& d){Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;}//...
}

<<    >>  重载

错误示例

class Date
{
//...//使用因为返回,为了适应连续输入或输出的情况ostream& operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;return out;}istream& operator>>(istream& in){in >>_year >>_month >>_day;return in;}//...
}

>> << 是二元操作符,上文中提到二元操作符第一个参数为左操作数,第二个参数为右操作数。此时这段代码第一个参数为this,也就意味着左操作数变成了对象,右操作数变成了cout。那么我们使用时只能这样写:

void Test2()
{Date d1(2023, 4, 1);d1 << cout;
}

虽然能满足需求,但是用起来感觉怪怪的。由于我们无法改变this的位置,所以只能使用其它办法来实现<< >>的重载了。

这里我们只能将重载定义在类的外面才能避开this的影响。但是类外的函数又访问不了类的私有成员。我们只能通过将重载函数设置为类友元函数来实现了。

class Date
{
//...//申明友元函数friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);//...
}
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";return out;
}istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

默认成员函数——赋值运算符重载

与之前讲的构造函数与析构函数等默认成员函数相同,赋值运算符重载也属于6个默认成员函数之一。

1. 作为与众不同的默认成员函数,其有以下特性:

  • 赋值运算符重载格式:
  • 参数类型:const T&,传递引用可以提高传参效率;
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值;
  • 返回*this :要复合连续赋值的含义;

赋值重载

class Date
{
//...Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}//...
}

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

错误示例

class Date
{//...
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

此种情况会出现编译错误error C2801: “operator =”必须是非静态成员。

  • 出错原因是:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

这里赋值重载与拷贝构造函数的特性非常相似。
 

默认成员函数——取地址操作符重载

6个默认成员函数只剩两个——取地址重载与const取地址重载。但是,这两个函数实在没有实现的必要,因为我们自己实现与编译器自动实现出来的效果是一样的。

class Date
{//...Date* operator&(){return this;}const Date* operator&()const{return this;}//...
};

const成员

将const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

什么情况下需要用const修饰?

我们可能暂时感受不到const修饰的作用,但是遇到如下情况,const修饰就非常有必要了。

class Date
{
public://...void print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
}void Test3()
{Date d1(2023, 4, 1);d1.print();const Date d2(2022, 3, 1);d2.print();
}

报错内容为:“void Date::print(void)”: 不能将“this”指针从“const Date”转换为“Date &”。

这里是典型的权限放大错误,我们不能将const Date* &d2传递给形参Date* this。

改正的办法为同样用const修饰this,但具体的写法可不像我们想的那样。

void print() const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

因为我们无法显式的修改this,所以C++规定在函数的后面加上const即为修饰this

附录

我们总结上文中的运算符重载,整理一下完整的日期类的实现。此处我们使用多文件的形式实现—>

  • Date.h文件中进行头文件包含命名空间展开类的声明内联函数定义等;
  • Date.cpp文件中进行对类成员函数的定义。
#define _CRT_SECURE_NO_DEPRECATE 1
#include<iostream>
#include<assert.h>
using namespace std;// 类里面短小函数,适合做内联的函数,直接是在类里面定义的
class Date
{// 友元函数声明friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 0, int month = 0, int day = 0);void Print() const;int GetMonthDay(int year, int month) const;bool operator==(const Date& d) const;bool operator!=(const Date& d) const;bool operator<(const Date& d) const;bool operator<=(const Date& d) const;bool operator>(const Date& d) const;bool operator>=(const Date& d) const;Date& operator+=(int day);Date operator+(int day) const;Date& operator-=(int day);Date operator-(int day) const;int operator-(const Date& d) const;Date& operator=(const Date& d);//前置++Date& operator++();// 后置++// int参数 仅仅是为了占位,跟前置重载区分Date operator++(int);// 前置--Date& operator--();// 后置--Date operator--(int);//取地址重载Date* operator&();const Date* operator&() const;private:int _year;int _month;int _day;
};inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
#define _CRT_SECURE_NO_DEPRECATE 1
#include"Date.h"
//构造函数
Date::Date(int year, int month , int day)
{//判断日期是否合法if (month > 0 && month < 13 &&(day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}
}bool Date::operator==(const Date& d) const
{return (_year == d._year) && (_month == d._month) && (_day == d._day);
}bool Date::operator<(const Date& d) const
{return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);
}bool Date::operator<=(const Date& d) const
{//函数的复用return *this < d || *this == d;
}bool Date::operator>(const Date& d) const
{//函数的复用return !(*this <= d);
}bool Date::operator>=(const Date& d) const
{//函数的复用return !(*this < d);
}bool Date::operator!=(const Date& d) const
{//函数的复用return !(*this == d);
}//获取当月的天数
int Date::GetMonthDay(int year, int month) const
{assert(month > 0 && month < 13);int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//判断是否是闰年的二月if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return monthArray[month];}
}//+= 返回自身的引用
Date& Date::operator+=(int day)
{//判断是否加了负数if (day < 0){//复用*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);//进位_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}Date& Date::operator-=(int day)
{//判断是否减了一个负数if (day < 0){//复用*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator+(int day) const
{//拷贝构造//因为加不改变自身的值,所以创建临时对象Date tmp(*this);//复用tmp += day;return tmp;
}Date Date::operator-(int day) const
{Date tmp(*this);tmp -= day;return tmp;
}//前置++
Date& Date::operator++()
{*this += 1;return *this;
}//后置++
// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,
// 故需在实现时需要先将this保存一份,然后给this + 1
// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}//后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}int Date::operator-(const Date& d) const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}Date& Date::operator=(const Date& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}Date* Date::operator&() 
{return this;
}
const Date* Date::operator&() const
{return this;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/209768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Vue实现一个当鼠标悬浮时出现,鼠标离开时消失的双层菜单

前言 分享一个基于Vue实现一个当鼠标悬浮时出现&#xff0c;鼠标离开时消失的双层菜单。同时也是为了以后需要类似的需求时&#xff0c;可以提供一个实现思路&#xff0c;以及可以快速ctrlc和ctrlv操作&#xff0c;提高工作效率~ 一、示例代码 &#xff08;1&#xff09;/sr…

Linux C/C++ 分析网络流量(十六进制TCP数据包分析)

在分析TCP数据包时&#xff0c;理解TCP协议的工作原理和报文格式是关键。TCP是一种面向连接的、提供可靠的、端到端的字节流传输服务。其头部结构包括源端口、目标端口、序列号、确认应答号等字段。序列号是在建立连接时由计算机生成的随机数作为初始值&#xff0c;每发送一次数…

使用Pytorch从零开始实现BERT

生成式建模知识回顾: [1] 生成式建模概述 [2] Transformer I&#xff0c;Transformer II [3] 变分自编码器 [4] 生成对抗网络&#xff0c;高级生成对抗网络 I&#xff0c;高级生成对抗网络 II [5] 自回归模型 [6] 归一化流模型 [7] 基于能量的模型 [8] 扩散模型 I, 扩散模型 II…

前端食堂技术周刊第 107 期:技术播客节、Deno Cron、FEDAY、XState v5、Electron 2023 生态系统回顾

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;烤椰拿铁 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

图论与网络优化3

CSDN 有字数限制&#xff0c;因此笔记分别发布&#xff0c;目前&#xff1a; 【笔记1】概念与计算、树及其算法【笔记2】容量网络模型、遍历性及其算法【笔记3】独立集及其算法 6 独立集及其算法 6.1 独立集和覆盖 6.1.1 独立数和覆盖数 独立集&#xff1a;设 S ⊆ V ( G …

PaddleDetection系列2--NCCL安装及测试

NCCL安装及测试 1 系统信息查看1.1 查看本机的操作系统和位数信息&#xff1a;1.2 确认处理器架构1.3 确认cuda版本 2 NCCL安装2.1 根据上面的系统架构以及CUDA版本&#xff0c;进入[官网](https://developer.nvidia.com/nccl/nccl-download)下载匹配的nccl&#xff0c;若想获取…

力扣44题通配符匹配题解

44. 通配符匹配 - 力扣&#xff08;LeetCode&#xff09; 给你一个输入字符串 (s) 和一个字符模式 (p) &#xff0c;请你实现一个支持 ? 和 * 匹配规则的通配符匹配&#xff1a; ? 可以匹配任何单个字符。* 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 …

【ITK库学习】使用itk库进行图像滤波ImageFilter:梯度Gradient

目录 1、itkGradientImageFilter2、itkGradientMagnitudeImageFilter 梯度强度3、itkGradientMagnitudeRecursiveGaussianImageFilter 带滤波的梯度强度4、itkDerivativeImageFilter 不带滤波的导函数 1、itkGradientImageFilter 该类是一个基类&#xff0c;用于使用方向导数计…

C++笔试题之回文数的判断

“回文”是指正读反读都能读通的句子&#xff0c;它是古今中外都有的一种修辞方式和文字游戏&#xff0c;如“我为人人&#xff0c;人人为我”等。在数学中也有这样一类数字有这样的特征&#xff0c;成为回文数&#xff08;palindrome number&#xff09;。 设n是一任意自然数…

MSSQL 程序集使用方法

1.C# 写一个程序 1.1新建一个项目【类库【.Net FrameWork】 1.2编写代码 删除 namespace ApiSQLClass { } 代码如下&#xff1a;【具体调用API模式根据具体编写】 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.…

1. 使用poll或epoll创建echo服务器

1. 说明&#xff1a; 此篇博客主要记录一种客户端实现方式&#xff0c;和两种使用poll或者epoll分别创建echo服务器的方式&#xff0c;具体可看代码注释&#xff1a; 2. 相关代码&#xff1a; 2.1 echoClient.cpp #include <iostream> #include <cstdio> #incl…

C语言中的 sizeof 运算符

在 C 语言中&#xff0c;sizeof 是一个运算符&#xff0c;用于获取给定类型或变量的字节大小。它返回一个 size_t 类型的值&#xff0c;表示以字节为单位的对象大小。 sizeof 运算符有以下特点&#xff1a; 用法&#xff1a;sizeof 运算符可以应用于数据类型或表达式。计算静…

酷开科技以创新为动力用大数据提升品牌认知

在21世纪的今天&#xff0c;我们生活在一个被互联网深深改变的世界。互联网不仅改变了我们的生活方式&#xff0c;也正在改变我们的思维方式和工作方式。而互联网作为一种新的发展趋势&#xff0c;更是为我们提供了无数的机会和无限可能性&#xff0c;从电子商务时代到社交网络…

CSP-何以包邮?

题目描述 新学期伊始&#xff0c;适逢顿顿书城有购书满 x 元包邮的活动&#xff0c;小 P 同学欣然前往准备买些参考书。 一番浏览后&#xff0c;小 P 初步筛选出 n 本书加入购物车中&#xff0c;其中第 i 本&#xff08;1≤i≤n&#xff09;的价格为 ai 元。 考虑到预算有限&am…

scala编码

1、Scala高级语言 Scala简介 Scala是一门类Java的多范式语言&#xff0c;它整合了面向对象编程和函数式编程的最佳特性。具体来讲Scala运行于Java虚拟机&#xff08;JVM)之上&#xff0c;井且兼容现有的Java程序&#xff0c;同样具有跨平台、可移植性好、方便的垃圾回收等特性…

ubuntu server 20.04 备份和恢复 系统 LTS

ubuntu server 20.04 备份和恢复 系统 LTS tar命令系统备份与恢复&#xff08;还原or新装&#xff09; 备份系统 cd / su root tar cvpzf backup.tgz --exclude/tmp --exclude/run --exclude/dev --exclude/snap --exclude/proc --exclude/lostfound --exclude/backup.tgz …

启动游戏出现concrt140.dll错误的8种解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是找不到concrt140.dll文件。这个错误通常会导致程序无法正常运行&#xff0c;给用户带来困扰。本文将介绍找不到concrt140.dll无法继续执行代码的8个方法&#xff0c;同时探讨concrt140.dll丢…

LinuxBasicsForHackers笔记 -- 文件系统和存储设备管理

设备目录/dev Linux 有一个特殊的目录&#xff0c;其中包含代表每个连接设备的文件&#xff1a;相应命名的 /dev 目录。 /dev中有很多设备列表。 特别令人感兴趣的是设备 sda1、sda2、sda3、sdb 和 sdb1&#xff0c;它们通常是硬盘驱动器及其分区以及 USB 闪存驱动器及其分区…

理解基于 Hadoop 生态的大数据技术架构

转眼间&#xff0c;一年又悄然而逝&#xff0c;时光荏苒&#xff0c;岁月如梭。当回首这段光阴&#xff0c;不禁感叹时间的匆匆&#xff0c;仿佛只是一个眨眼的瞬间&#xff0c;一年的旅程已成为过去&#xff0c;而如今又到了画饼的时刻了 &#xff01; 基于 Hadoop 生态的大数…