【C++技能树】令常规运算符用在类上 --类的六个成员函数II

news2024/9/20 16:12:03

在这里插入图片描述

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。

本篇导航

  • 0.运算符重载
  • 1.赋值运算符 = 重载
  • 2.比较运算符 == 重载
  • 3.比较运算符 != 重载
  • 4.比较运算符 < 重载
  • 5.比较运算符 <= 重载
  • 6. 比较运算符 > 重载
  • 7.比较运算符 >= 重载
  • 8. 赋值运算符 += 与 + 重载
  • 9.赋值运算符 -= 与 - 重载:
  • 10. 前置++与后置++
  • 11.前置--与后置--
  • 12.逻辑运算符-的重载
  • 13.流运算符重载
    • 13.1输出流重载:
    • 13.2输入流重载:
  • 14.完整代码:
  • 15. 取地址运算符重载
  • 16.至此六个内置类型成员函数完结

在这里插入图片描述

0.运算符重载

C++中为了增强代码的可读性,加入了运算符的重载,与其他函数重载一样。其命名格式如下:

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

Date operator<(Date&d1)

但并不是所有运算符都可以重载的:

  1. 不可以自定义出一个全新的操作符,如@
  2. .* :: sizeof ?: . 以上操作符不能重载,特别是第一个.*

接下来我们定义一个日期类来作为操作符重载的实践:

class Date{
public:
    Date(int day=1,int month=1,int year=1)
    {
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date&d)
    {
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy";
    }
private:
    int _year;
    int _month;
    int _day;
}

这是一个最基本的类函数,其包括一个默认构造函数,以及一个拷贝构造函数,拷贝构造函数会在调用时输出:“copy”,这将有助于我们后期分析函数.

1.赋值运算符 = 重载

先来看看他是怎么定义的吧.同样符合上方的定义法则:

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

为什么需要返回值调用呢?因为这样做可以节省空间。以引用的方式传参、返回值不需要调用拷贝构造函数

那this不是形式参数嘛?为什么出作用域还能被引用呢?*this本身是形式参数,但是 this指向的是date对象,其栈帧存储在Main函数中,所以this会被销毁,而返回的是this指向的对象,并不会被销毁

加入const只是一个保险,让我们不会改变传入的参数

我们可以以一段代码来演示一下:

class Date{
    
public:
    
   
    Date(int day=1,int month=1,int year=1)
    {
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date &d)
    {
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy";
    }

    Date& operator=(const Date &d)
    {
        _day=d._day;
        _month=d._month;
        _year=d._year;
        return *this;
    }
int main()
{ 
    Date d1(2,5,2024);
    Date d2;
    d2=d1;
}

先将两个引用都去掉,运行会发现,调用了两次拷贝构造,分别为传值以及返回值

在这里插入图片描述

**加上后则不会调用拷贝构造,所以这样写节省空间与时间。**在编译阶段,改代码会被转变成

d2.operator=(d1)

这也方便我们对这个操作符的具体执行方式进行理解。

至于为什么需要返回Date类型呢?考虑以下场景:

int a;
int b;
int c;
a=b=c=1;

这段代码运行的实质为:
在这里插入图片描述

所以我们需要令其返回Date类型来满足这种情况:

int main()
{
     
    Date d1(2,5,2024);
    Date d2;
    Date d3;
    d3=d2=d1;
}

赋值运算符如果不自己定义,系统就会自动生成一个赋值运算符(这与之前提到的构造函数、拷贝构造函数、析构函数),其调用规则业余之前一样,若没有定义则内置类型完成浅拷贝,自定义类型调用其自定义的赋值运算符重载,所以与调用拷贝构造函数相同,涉及空间管理的内置类型,我们需要自己定义拷贝构造函数。这也导致赋值运算符不能定义在全局,只能定义在类中,若在自己在全局定义一个就会和类中定义的冲突。

2.比较运算符 == 重载

bool operator==(const Date&d)const
    {
        if(_year==d._year&&_month==d._month&&_day==d._day)
        {
            return true;
        }
        return false;
    }

三个都相同则返回true,否则返回false,这整体逻辑很好理解。这个函数并不是默认函数,所以我们可以把它定义在全局,也可以把他定义在类中。

若定义在全局会出现类中私有成员无法访问的问题(这之后会解决,但现在我们为了规避这个问题我们先将其放在类中)

那这个const为什么放在外面呢?这里是对this指针,也就是本体的一个修饰,因为this指针是隐式的存在,且在这个重载中我们并不会改变本体,所以对其加上修饰

this指针原来的模样:无法改变this指针指向的内容,但可以改变其值

Date * const this

现在修饰后:既无法改变this指针指向的内容,也无法改变其值,对其起到了一个保护的作用

const Date * const this 

3.比较运算符 != 重载

 bool operator!=(const Date&d)const
    {
        if(*this==d)return false;
        return true;
    }

这里直接复用了上方的结果,也证明了重载运算符的强大之处hhhh

4.比较运算符 < 重载

 bool operator<(const Date &d)const
    {
        if(_year<d._year)
        {
            return true;
        }
        if(_year==d._year&&_month<d._month)
        {
            return true;
        }
        if(_year==d._year&&_month==d._month&&_day<d._day)
        {
            return true;
        }
        return false;  
    }

主要为逻辑的实现:

  1. 若年小,则直接返回true
  2. 若年相同,月小,直接返回true
  3. 若年月都相同,天数小,直接返回true

若不满足以上情况,则说明不小于,则直接返回false

5.比较运算符 <= 重载

 bool operator<=(const Date &d)const
    {
        return *this<d||*this==d;
    }

也是对之前写过的==以及<的一个复用.若小于或者等于则满足小于等于

6. 比较运算符 > 重载

bool operator>(const Date &d)const
{
     return (!(*this<d)&&(*this!=d))?true:false;
}

若不小于且不等于则满足小于等于

7.比较运算符 >= 重载

bool operator>=(const Date &d)const
    {
        return !(*this<d);
    }

若不小于则满足大于等于

8. 赋值运算符 += 与 + 重载

在实现接下来的重载中,为了方便获得每一个月份的具体日期,包括闰年平年的判断。在类中实现了一个getmonth函数用来返回每一个月具体天数。

int getmonth(int year,int month)
    {
        static int monthday[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
        if((year%4==0&&year%100!=0)||year%400==0)
        {
            if(month==2)return 29;
        }
        return monthday[month];
    }

传入年与月,进行判断具体日期数,其中闰年满足四年一闰,百年不闰,四百年一闰

接下来进行+=的实现:这里需要先判断下若Day<0,则等价于计算*this-=Day

Date& operator+=(int Day)
    {
    	if(Day<0)
        {
            *this-=(-Day);
            return *this;
        }
        _day+=Day;
        while(_day>getmonth(_year,_month))
        {
            _day-=getmonth(_year,_month);
            ++_month;
            if(_month>12)
            {
                _month=1;
                _year++;
            }
        }
        return *this;
    }

具体实现逻辑如下:
在这里插入图片描述

那么我们来看看+的逻辑:

Date operator+(int day)
    {
        Date tmp=*this;
        tmp+=day;
        tmp.print();
        return tmp;
    }

为了不改变Date对象的本身,需要先将Date复制一份(这里使用拷贝构造函数,而不是赋值运算符重载,因为其在初始化对象时赋值),然后复用+=,最后返回其本身.因为是局部参数,所以并不能返回引用,因为tmp出作用域会被销毁.

9.赋值运算符 -= 与 - 重载:

-=:

 Date& operator-=(const int Day)
    {
        if(Day<0)
        {
            *this+=(-Day);
            return *this;
        }
        _day-=Day;
        while(_day<=0)
        {
            --_month;
            if(_month<1)
            {
                _year--;
                _month=12;
            }
            _day+=getmonth(_year,_month);
        }
        return *this;
    }

-:

Date operator-(const int Day)
    {
        Date tmp=*this;
        return tmp-=Day;
    }

整体逻辑与加法大差不差,不过多赘述.

10. 前置++与后置++

前置++

Date& operator++()
    {
        *this+=1;
        return *this;
    }

若我想要将一个Date对象执行++操作,则直接:++Date即可.

后置++:为了和前置++再一次发生重载,加入了一个参数,这个参数在重载过程中并不会起到什么作用,仅为分辨具体是哪个重载,使用时编译器会自己在后置++中传入0,我们仅需正常使用即可,

Date operator++(int)
    {
        Date tmp=*this;
        *this+=1;
        return tmp;
    }

在刚学习c的阶段,总会看到讨论关于前置++与后置++的讨论,现在看重载就知道了,因为后置++会拷贝一份原始值,所以效率会低一些.

不过在内置类型中,这种效率变换可以忽略不计.

11.前置–与后置–

 Date& operator--()
    {
        *this-=1;
        return *this;
    }

    Date operator--(int)
    {
        Date tmp;
        *this-=1;
        return tmp;
    }

12.逻辑运算符-的重载

为两个日期类间想减,计算日期间的差值

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

先找出最大的日期,通过将最小的++与计数天数++,一步步逼近最大的天数,最后返回,注意传值需要传引用,提高效率

13.流运算符重载

为什么在c++中cout/cin可以输出/输入任意内置类型的对象,而不用带对象类型呢?因为在c++中cout与cin也为一个流对象,其底层实现了多个类型的重载

此重载需要定义在全局中,若定义在类中,根据上方的转换法则,则会变成d1<<cout,但定义在全局中,会出现访问不了私有变量的情况,所以我们提供了一个友元函数的声明.

  friend ostream& operator<<(ostream& out,const Date&d);
  friend istream& operator>>(istream& in, Date&d);

放在类中哪里都可以,表示这个函数是这个类的朋友,可以直接访问其中的变量与函数(关于友元的具体含义之后会细讲,)

13.1输出流重载:

ostream& operator<<(ostream& out,const Date&d)
{
   out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
   return out;
}

ostream为输出流对象,其返回值会放进输出流在输出

13.2输入流重载:

istream& operator>>(istream& in,Date&d)
{
   	int year, month, day;
	in >> day >> month >>year;

	if (month > 0 && month < 13
		&& day > 0 && day <= d.getmonth(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
	}
	return in;
}

整体与上方相同

14.完整代码:

#include<iostream>
using namespace std;
class Date{
    
public:
    
    friend ostream& operator<<(ostream& out,const Date&d);
    friend istream& operator>>(istream& in, Date&d);
   
    Date(int day=1,int month=1,int year=1)
    {
        if(month>0&&month<13&&day>0&&day<getmonth(year,month))
            _day=day,_month=month,_year=year;
        else
            cout<<"输入错误";
    }

    Date(const Date&d)
    {
        _day=d._day;
        _month=d._month;
        _year=d._year;
        cout<<"copy"<<endl;
    }

    int getmonth(int year,int month)
    {
        static int monthday[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
        if((year%4==0&&year%100!=0)||year%400==0)
        {
            if(month==2)return 29;
        }
        return monthday[month];
    }

    void print()
    {
        cout<<"day: "<<_day<<" month: "<<_month<<" year: "<<_year<<endl;
    }


    Date operator=(const Date d)
    {
        _day=d._day;
        _month=d._month;
        _year=d._year;
        return *this;
    }


    Date& operator+=(int Day)
    {
        if(Day<0)
        {
            *this-=(-Day);
            return *this;
        }
        _day+=Day;
        while(_day>getmonth(_year,_month))
        {
            _day-=getmonth(_year,_month);
            ++_month;
            if(_month>12)
            {
                _month=1;
                _year++;
            }
        }
        return *this;
    }
    Date operator+(int day)
    {
        Date tmp=*this;
        tmp+=day;
        tmp.print();
        return tmp;
    }
    Date& operator++()
    {
        *this+=1;
        return *this;
    }
    Date operator++(int)
    {
        Date tmp=*this;
        *this+=1;
        return tmp;
    }

    int operator-(Date &d)
    {
        Date max=*this;
        Date min=d;
        int flag=1;
        if(max<min)
        {
            max=d;
            min=*this;
            flag=-1;
        }
        int n=0;
        while(min!=max)
        {
            ++min;
            ++n;
        }
        return n*flag;
    }
    
    Date& operator-=(const int Day)
    {
        if(Day<0)
        {
            *this+=(-Day);
            return *this;
        }
        _day-=Day;
        while(_day<=0)
        {
            --_month;
            if(_month<1)
            {
                _year--;
                _month=12;
            }
            _day+=getmonth(_year,_month);
        }
        return *this;
    }
    
    Date operator-(const int Day)
    {
        Date tmp=*this;
        return tmp-=Day;
    }

    Date& operator--()
    {
        *this-=1;
        return *this;
    }

    Date operator--(int)
    {
        Date tmp;
        *this-=1;
        return tmp;
    }



    bool operator==(const Date&d)const
    {
        if(_year==d._year&&_month==d._month&&_day==d._day)
        {
            return true;
        }
        return false;
    }
    bool operator!=(const Date&d)const
    {
        if(*this==d)return false;
        return true;
    }
    bool operator<(const Date &d)const
    {
        if(_year<d._year)
        {
            return true;
        }
        if(_year==d._year&&_month<d._month)
        {
            return true;
        }
        if(_year==d._year&&_month==d._month&&_day<d._day)
        {
            return true;
        }
        return false;  
    }

    bool operator<=(const Date &d)const
    {
        return *this<d||*this==d;
    }

    bool operator>(const Date &d)const
    {
        return (!(*this<d)&&(*this!=d))?true:false;
    }
    bool operator>=(const Date &d)const
    {
        return !(*this<d);
    }


private:
    int _year;
    int _month;
    int _day;
};

ostream& operator<<(ostream& out,const Date&d)
{
   out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
   return out;
}

istream& operator>>(istream& in,Date&d)
{
   	int year, month, day;
	in >> day >> month >>year;

	if (month > 0 && month < 13
		&& day > 0 && day <= d.getmonth(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
	}
	return in;
}

int main()
{
     
    Date d1(2,5,2024);
    d1+=100;
    d1.print();
}

至此日期类整体完成

15. 取地址运算符重载

class Date
{
public :
	Date* operator&()
	{
		return this ;
	}
	const Date* operator&()const
    {
    	return this ;
    }
private :
    int _year ; 
    int _month ; 
    int _day ; 
};  

默认情况下,编译器会自动生成这个重载,大多数情况下我们也不需要写这个重载.

若不想让一个人获得可修改地址,仅想让其获得不可修改的const地址,可以这样写

class Date
{
public :
	Date* operator&()
	{
		return NULL ;
	}
	const Date* operator&()const
    {
    	return this ;
    }
private :
    int _year ; 
    int _month ; 
    int _day ; 
};  

16.至此六个内置类型成员函数完结

img

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

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

相关文章

图片处理软件:分享6款非常实用的图片处理工具

目录 一、移动端 1、snapseed 2、一键抠图 3、pixlr 二、电脑端 1、图片编辑助手 2.GIMP 3、photopea 今天给大家分享6款非常实用的图片处理工具&#xff0c;其中包含移动端和电脑端&#xff0c;每一款都非常实用&#xff0c;希望对大家能有所帮助&#xff01; 一、移…

《编程思维与实践》1059.计算a的n次方的大整数

《编程思维与实践》1059.计算a的n次方的大整数 题目 思路 高精度的问题统一的解决思路是用一个数组去存大整数的每一位数,运算转化为对数组的操作. 可以从个位开始存(逆序),也可以从最高位开始存(顺序),以处理方便为主要考虑因素. 同时可以将大整数定义为一个结构体,包含位数,…

软件架构:理解分析三层结构观点

三层结构的简单描述及优点   三层体系结构&#xff0c;即用户层、应用层和数据库服务器。用户层主要指用户界面&#xff0c;它要求尽可能的简单&#xff0c;使最终用户不需要进行任何培训就能方便地访问信息&#xff1b;第二层就是应用服务器&#xff0c;也就是常说的中间件&…

webpack: 4 loader汇总(style-loader等)

所有的loader必须匹配规则&#xff0c;否则不生效 配置文件中&#xff0c;module中rules的use执行顺序是从后往前执行 url-loader 用于将文件转换为base64 URI的webpack加载程序。 options limit limit指定文件大小&#xff0c;小于limit的图片不会生成图片以base64格式被引入…

客观地说,应该看一看 Web3.0 了

武术圈有名言&#xff1a;“八极加劈挂&#xff0c;神鬼都害怕”。要是 Web3.0AGI 的话&#xff0c;世界将会变成什么样子&#xff1f; 数科星球原创作者丨苑晶编辑丨大兔 Web3.0 的价值开始受到重视&#xff0c;在最近&#xff0c;来自香港的好消息再次带火了这个领域的热度。…

VMware NSX-T Data Center 3.2.3 - 数据中心网络全栈虚拟化

VMware NSX-T Data Center 3.2.3 - 数据中心网络全栈虚拟化 重要更新&#xff1a;修复 136 个 bug。 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-nsx-t-3/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VMwa…

ChatGPT 接入飞书教程,创建自己的聊天机器人

ChatGPT 接入飞书教程,创建自己的聊天机器人 一、飞书进入开发者平台。点击创建应用。二、打开Aircode,点击创建应用,上面输入名字,下面选择Node.js v16三、配置环境,点击Environments,创建四个变量,全部要大写本教程收集于: AIGC从入门到精通教程 首先,准备三个账号…

FPGA实现MPEG2视频压缩PCIe传输 提供软硬件工程源码和技术支持

目录 1、前言2、MPEG2视频压缩实现3、我已有的FPGA图像视频编解码方案4、我已有的PCIE方案5、MPEG2视频压缩PCIE传输设计方案FPGA硬件设计软件设计 6、Vivado工程详解7、Linux下的XDMA驱动安装8、上板调试验证9、福利&#xff1a;工程代码的获取 1、前言 MJPEG、MPEG2、MPEG4、…

IOS开发指南之storyboard中控件与变量关联及控件事件与方法关联

1.创建IOS工程 2.选择 interface为storyboard,语言选择Objective-C 打开工程后可看到工程默认添加了两个storyboard文件 Main.storyboard为应用主场景,LaunchScreen为启动屏幕场景 3.双击Main.storyboard打开设计窗口,并拖放控件布局如下 4.为控件添加关联变量与(IBOutlet与I…

身高测量仪红外传感测距模块应用方案 WTU201F2 B004 低功耗

​身高测量仪被广泛用于医疗、教育等领域&#xff0c;而红外测距模块在身高测量仪中的应用则成为了一种新的技术手段。红外测距模块是基于红外线技术的一种测距器件&#xff0c;在身高测量仪中&#xff0c;红外测距模块能够精准地测量出人体的高度。与传统的测量方法相比&#…

品牌如何从零开始运营抖音,带你全面了解

随着短视频逐渐深入我们的生活&#xff0c;巨大的流量吸引了众多人群。很多人因此也想开始做抖音&#xff0c;很多品牌却因为内容制作流程复杂&#xff0c;不知从何入门。今天&#xff0c;和大家分享下品牌如何从零开始运营抖音。 其实在开始一件事情之前&#xff0c;最主要的是…

智能感测离子风棒,托起中国“智”造新高度

智能感测离子风棒采用市面上较为少见的内置感测型离子发生器&#xff0c;能够对外界环境电荷进行快速平衡。该设备智能控制&#xff0c;无需人工点检&#xff0c;达到全自动静电消除并具备异常报警功能。能让ESD管理者或使用者先一步发现问题&#xff0c;快速准确的解决问题 。…

Shell脚本例题

1.计算从1到100所有整数的和 2.提示用户输入一个小于100的整数&#xff0c;并计算从1到该数之间所有整数的和 3.求从1到100所有整数的偶数和、奇数和 4.用户名存放在users.txt文件中&#xff0c;每行一个&#xff0c;判断文件里的用户是否存在&#xff0c;若该用户存在&#xf…

京喜APP - 图片库优化 | 京东云技术团队

作者&#xff1a;京东零售 何骁 介绍 京喜APP早期开发主要是快速原生化迭代替代原有H5&#xff0c;提高用户体验&#xff0c;在这期间也积累了不少性能问题。之后我们开始进行一些性能优化相关的工作&#xff0c;本文主要是介绍京喜图片库相关优化策略以及关于图片相关的一些…

es 7.0.8 常用DSL语句进行索引crud操作(windows es7.x)

一 es7.x的核心 1.1 es的核心概念 1.ES 里的 Index 可以看做一个库(名称必须为小写)&#xff0c;而 Types 相当于表&#xff0c;Documents 则相当于表的行。 2.这里 Types 的概念已经被逐渐弱化&#xff0c;Elasticsearch 6.X 中&#xff0c;一个 index 下已经只能包含一个…

postgresql数据库linux centos7 安装

简介 &#xff08;百度百科&#xff09; PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统&#xff08;ORDBMS&#xff09;&#xff0c;是以加州大学计算机系开发的POSTGRES&#xff0c;4.2版本为基础的对象关系型数据库管理系统。POSTGRES的许多领先概念只…

Linux基础学习---1、目录结构、Vim编辑器、网络配置和远程登录

1、Linux目录结构 1.1 /bin 是Binary的缩写&#xff0c;这个目录存放着最经常使用的命令。1.2 /sbin s是Super User的意思&#xff0c;这里存放的是系统管理员使用的系统管理程序。1.3 /home 存放普通用户的主目录&#xff0c;在Linux中每个用户都有一个自己的目录&#xff…

海睿思分享 | 浅谈智能数据治理

随着大数据和AI技术的发展&#xff0c;利用企业内外部海量数据进行融合、治理、分析和应用&#xff0c;已成为企业经营管理越来越重要的手段。如何构建企业大数据分析平台发挥数据价值正成为企业数字化转型的核心战略任务。 基于大数据平台进行数据分析的完整流程分为&#xf…

MyBatis--映射关系一对一和MyBatis--映射关系多对一 -都有基于xml和注解的教程

目录 MyBatis--映射关系一对一 映射关系-官方文档 映射关系1 对1-基本介绍 注意细节 映射方式 方式1 创建idencardv 表 创建person表 创建新的 module(mybatis-mapping), 相关配置文件可以从上一个 module 拷贝 创建IdenCard.java对应表类 创建Person.java对应表类…

Vue2相关面试题(持续更新)

前言 目前这套面试题只适合 初级前端&#xff0c;后面会进行深层次补充和拓展以及Vue2源代码的讲解&#xff08;虽然Vue2今年开始不维护了&#xff0c;但是我的面试题不会止步&#xff0c;冲冲冲&#xff09;。在面试的过程中&#xff0c;一定要清楚哪些该说哪些不该说&#x…