C++【类和对象】(取地址运算符重载与实现Date类)

news2024/9/29 23:29:35

文章目录

  • 取地址运算符重载
    • const成员函数
    • 取地址运算符重载
  • Date类的实现
    • Date.h
    • Date.cpp
      • 1.检查日期合法性
      • 2. 构造函数/赋值运算符重载
      • 3.得到某月的天数
      • 4. Date类 +- 天数的操作
        • 4.1 日期 += 天数
        • 4.2 日期 + 天数
        • 4.3 日期 -= 天数
        • 4.4 日期 - 天数
      • 5. Date的前后置++/--
        • 5.1 前置++
        • 5.2 后置++
        • 5.3 前置--
        • 5.4 后置--
      • 6. Date类之间的比较
        • 6.1 < 操作符
        • 6.2 ==操作符
        • 6.3 <=操作符
        • 6.4 >操作符
        • 6.5 >=操作符
        • 6.6 !=操作符
      • 7.日期-日期
      • 8. Date的流插入与流提取
        • 8.1流插入函数
        • 8.2 流提取函数
    • Test.cpp
  • 结语

取地址运算符重载

const成员函数

  • 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
  • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由Date* const this变成 const Date* const this

还是拿Date类举例,如果我用const 修饰的对象来调用Print会发生报错,原因是Print隐含的this指针是Date* const this,当我用const对象来调用Print会发生权限放大。
在这里插入图片描述
但是用const修饰成员函数,就可以解决问题,因为用const修饰后Print隐含的this指针由Date* const this变成 const Date* const this,就不会出现权限放大。

#include<iostream>
using namespace std;
class Date
{
public:

    Date(int year = 1, int month = 1, int day = 1)
    {
        //cout << "这是构造函数" << endl;
        _year = year;
        _month = month;
        _day = day;
    }

    //void Print(const Date* const this)
    void Print() const
    {
        cout << _year << '/' << _month << '/' << _day << endl;
    }

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

在这里插入图片描述

取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非一些很特殊的场景,比如我们不想让别⼈取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。

    Date* operator&()
    {
        //return nullptr;(胡乱给的地址)
        return this;
    }

    const Date* operator&() const
    {
        //return nullptr;(胡乱给的地址)
        return this;
    }

Date类的实现

我们在实现一个任务的时候,可以让声明与定义分离,这是一个好习惯。

Date.h

#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
    //1.检查日期的合法性
    bool CheckDate();
    //2.构造函数与赋值运算符重载
    Date(int year = 1, int month = 1, int day = 1);
    Date& operator=(const Date& d);


    bool LeapYear(int year)
    {
        if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return true;
        }
        return false;
    }
    //3.得到某月天数
    int GetMonthDay(int year, int month)
    {
        static int month_day[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (month == 2 && LeapYear(year))
        {
            return month_day[month] + 1;
        }
        return month_day[month];
    }



    //4. Date类 +- 天数的操作
    Date operator+(const int day);
    Date& operator+=(const int day);
    Date operator-(const int day);
    Date& operator-=(const int day);

	//5. Date的前后置++/--
    Date& operator++();
    Date operator++(int);

    Date& operator--();
    Date operator--(int);
	
    //6. Date类之间的比较
    bool operator<(const Date& d);
    bool operator<=(const Date& d);
    bool operator==(const Date& d);
    bool operator>(const Date& d);
    bool operator>=(const Date& d);
    bool operator!=(const Date& d);

    void Print();
    
    //7. 日期-日期(算相差多少天)
    int operator-(const Date& d);
    
    //8. 日期的流插入与流提取
    friend ostream& operator<<(ostream& out, Date& d);
    friend istream& operator>>(istream& in, Date& d);

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

Date.cpp

实现前要包头文件

#include"Date.h"

1.检查日期合法性

	//检擦日期合法性
    bool Date:: CheckDate()
    {
        if (_month <= 0 || _month >= 13 || 
            _day > GetMonthDay(_year, _month) || _day <= 0)
        {
            return false;
        }

        return true;
    }

2. 构造函数/赋值运算符重载

构造函数

    Date::Date(int year, int month, int day)
    {
        //cout << "这是构造函数" << endl;
        _year = year;
        _month = month;
        _day = day;

        while (!CheckDate())
        {
            cout << "非法日期" << endl;
            cout << *this;

            cout << "请重新输入日期" << endl;
            cin >> *this;
        }

    }

其实我们是不需要写拷贝构造和赋值运算符重载,因为Date类内部的成员对象并没有指向资源。
但是我们写赋值运算符重载是为了实现连续赋值。

    Date& Date::operator=(const Date&d)
    {
        //如果不是自己给自己赋值
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        //d1 = d2 应该返回前者(因为前者是被改变的) 所以要返回*this
        return *this;
    }
    

3.得到某月的天数

因为GetMonthDay这个函数会被频繁大量的调用,所以我们放在类里面定义(类里面的函数默认inline展开)

	//判断闰年(其实这个函数没必要写,但我为了美观就写了该函数)
    bool LeapYear(int year)
    {
        if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return true;
        }
        return false;
    }
    //得到某月的天数
    int GetMonthDay(int year, int month)
    {
    	//将month_day静态化,这样就不用反复的创建数组
        static int month_day[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (month == 2 && LeapYear(year))
        {
            return month_day[month] + 1;
        }
        return month_day[month];
    }

4. Date类 ± 天数的操作

加减的是天数

4.1 日期 += 天数

加等使用的是进位思想,当这个月满了,向下个月借天数(同时月份也会+1)当月份大于12时,就将年份+1,同时月份到一月,直到日期为合法。

    Date& Date::operator+=(const int day)
    {
        if (day < 0)
        {
            return *this -= (-day);
        }

        _day += day;
        
        while (_day > GetMonthDay(_year,_month))
        {
            int tmp = GetMonthDay(_year, _month);
            _day -= tmp;
            ++_month;
            if (_month > 12)
            {
                _month = 1;
                ++_year;
            }
        }

        return *this;
    }
4.2 日期 + 天数

这里我们可以直接复用 日期+=天数 !

    Date Date::operator+(const int day)
    {
        Date tmp(*this);
        tmp += day;

        return tmp;
    }
4.3 日期 -= 天数

-=与+=的实现十分类似,只不过-=使用的是退位的思想,向前一个月借天数(月份-1),当月份小于1,年份-1,月份到12月,直到日期合法

    Date& Date:: operator-=(const int day)
    {
        if (day < 0)
        {
            return *this += (-day);
        }
        _day -= day;

        while (_day <= 0)
        {
            --_month;
            if (_month == 0)
            {
                _month = 12;
                --_year;
            }
            int tmp = GetMonthDay(_year, _month);
            _day += tmp;

        }

        
4.4 日期 - 天数

和+一样,复用-=

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

5. Date的前后置++/–

前置不需要加入int形参,后置需要加入int形参。

5.1 前置++

复用+=

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

5.2 后置++

在参数位置上加一个int形参

    Date Date::operator++(int)
    {
        Date tmp = *this;
        *this += 1;
        return tmp;
    }
5.3 前置–
    Date& Date::operator--()
    {
        *this -= 1;
        return *this;
    }
5.4 后置–
    Date Date::operator--(int)
    {
        Date tmp = *this;
        *this -= 1;
        return tmp;
    }

6. Date类之间的比较

完成 < + == 或者 > + ==其他的比较就可以复用了。
本文实现的是 < + ==

6.1 < 操作符
    bool Date:: operator<(const Date& d)
    {
        if (_year > d._year)
        {
            return false;
        }
        else if (_year == d._year)
        {
            if (_month >= d._month)
            {
                return false;
            }
            else if (_month == d._month)
            {
                if (_day >= d._day)
                {
                    return false;
                }
            }
        }
        return true;
    }
6.2 ==操作符
   bool Date::operator==(const Date& d)
   {
       return _year == d._year &&
        _month == d._month &&
         _day == d._day;
   }
6.3 <=操作符
   bool Date::operator<=(const Date& d)
   {
       return (*this < d) || (*this == d);
   }
6.4 >操作符
    bool Date::operator>(const Date& d)
    {   
        return !(*this <= d);
    }
6.5 >=操作符
    bool Date::operator>=(const Date& d)
    {
        return !(*this < d);
    }
6.6 !=操作符
    bool Date::operator!=(const Date& d)
    {
        return !(*this == d);
    }

7.日期-日期

我们使用最简单的思路,取到区分较大和较小的日期,让较小的日期+1加到与较大的日期相等,同时记加了多少次,就得到了日期之间的天数。(cup的运行速度超级快,每秒上亿次,所以这点运算量对于cup来说轻轻松松)

    //Date-Date
    int Date::operator-(const Date& d)
    {
        int n = 0;
        int flag = 1;
        //假设法
        Date min = *this;
        Date max = d;
        if (min > max)
        {
            min = d;
            max = *this;
            flag = -1;
        }
        //计算天数
        while (min != max)
        {
            ++min;
            ++n;
        }
        return n * flag;
    }

8. Date的流插入与流提取

8.1流插入函数
ostream& operator<<(ostream& out, Date& d)
{
    cout << d._year << '/' << d._month << '/' << d._day << endl;

    return out;
}

注意,这里流插入和流提取都是全局函数,而非类的成员函数,其实我们在类里面也可以实现,但是会很怪。

由于成员函数的第一个参数默认是this,但运算符重载的规定是,当两个变量使用重载的运算符,会将左边的变量视为第一个参数,右边的变量视为第二个参数;这样的话,我们就不能写成cout << Date类实例化的对象,而是要写成Date类实例化的对象 << coutcin同理。

但是这样并不符合我们的习惯,所以我们设置成了全局函数,但是全局函数又有一个问题,就是Date类内部的成员变量是私有的,外部不能访问。
对此我们有以下几个方法:

  1. 将成员变量公有化(最最最不推荐的方法)
  2. 用get函数来得到成员变量
  3. 使用友元函数
  4. 放到类的内部(因参数问题不考虑)

这里我们使用的是友元函数(后面会详细讲解)。

8.2 流提取函数
istream& operator>>(istream& in, Date& d)
{
    cin >> d._year >> d._month >> d._day;
    while (!d.CheckDate())
    {
        cout << "非法日期" << endl;
        cout << d;

        cout << "请重新输入日期" << endl;
        cin >> d;
    }
    return in;
}

Test.cpp

#include"Date.h"

void Test1()
{
    Date d1;
    Date d2(2026, 2, 28);
    //Date d3(2029, 9, 33);
    Date d3(2029, 9, 30);
    d1.Print();
    d2.Print();
    d3.Print();

    d3 = d2 = d1;

    d1.Print();
    d2.Print();
    d3.Print();
}

void Test2()
{
    Date d1(2026, 2, 28);

    d1 += 10000;
    d1.Print();

    d1 -= 10000;
    d1.Print();

    Date d2 = d1 + 10000;
    d2.Print();

    Date d3 = d1 - 10000;
    d3.Print();
}

void Test3()
{
    Date d1(2026, 2, 28);

    Date d2 = ++d1;
    d2.Print();

    Date d3 = d1++;
    d3.Print();    

    Date d4 = --d1;
    d4.Print();   

    Date d5 = d1--;
    d5.Print();
}
void Test4()
{
    Date d1(2024, 9, 28);
    Date d2(2026, 2, 28);
    cout << (d1 == d2) << endl;
    cout << (d1 != d2) << endl;
    cout << (d1 < d2) << endl;
    cout << (d1 <= d2) << endl;
    cout << (d1 > d2) << endl;
    cout << (d1 >= d2) << endl;
}

void Test5()
{
    Date d1(2024, 9, 28);
    Date d2(2026, 2, 28);

    cout << d1 - d2 << endl;
}

void Test6()
{
    Date d1;
    cin >> d1;
    cout << d1;
}

int main()
{
    //Test1();
    //Test2();
    //Test3();
    //Test4();
    //Test5();
    Test6();
    return 0;
}

结语

这次的分享就到这里结束了~
最后感谢您能阅读完此片文章~
如果您认为这篇文章对你有帮助的话,可以用你们的手点一个免费的赞并收藏起来哟~
如果有任何建议或纠正欢迎在评论区留言~
也可以前往我的主页看更多好文哦(点击此处跳转到主页)。

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

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

相关文章

学习鸿蒙HarmongOS(基础一)

最近听到一个朋友在干鸿蒙系统开发&#xff0c;于是我也来看看&#xff0c;我看到的第一感觉和前端TS好像&#xff0c;鸿蒙的是叫ArkTS&#xff0c;于是来看一下视频&#xff0c;学习了一下&#xff0c;我的随手笔记记录一下吧,方便我以后阅读 基本 语句 函数

unity3D雨雪等粒子特效不穿透房屋效果实现

做项目有时候会做天气模拟&#xff0c;模拟雨雪天气等等。但是容易忽略一个问题&#xff0c;就是房屋内不应该下雨或者下雪&#xff0c;这样不就穿帮了嘛。 下面就粒子穿透物体问题做一个demo。 正常下雨下雪在室内的话&#xff0c;你可以看到&#xff0c;粒子是穿透建筑的。…

【C++篇】启航——初识C++(上篇)

目录 引言 一、C的起源和发展史 1.起源 2.C版本更新 二、C在⼯作领域中的应⽤ 三、C入门建议 1.参考文档 2.推荐书籍 四、C的第一个程序 1.C语言写法 2.C写法 五、命名空间 1.为什么要有命名空间 2.定义命名空间 3.主要特点 4.使用示例 六、C输⼊&输出 …

C程序设计——结构化程序设计的三种结构

前面我说过&#xff1a;“结构化编程语言&#xff0c;用语法限制程序员&#xff0c;只能使用顺序、选择、循环三种结构来解决问题。” 接下来&#xff0c;就讲解这三种结构。 顺序结构 前面我讲过&#xff0c;C语言所有的程序&#xff0c;都必须有一个 main 函数&#xff0c…

TCP\IP标准与OSI标准

TCP/IP 模型和 OSI 模型都是用于描述网络体系结构的模型&#xff0c;但它们的设计理念和层次结构有所不同。TCP/IP 模型更注重实际实现&#xff0c;而 OSI 模型更注重抽象和标准化。 1. OSI 模型 (Open Systems Interconnection Model) OSI 模型是一个七层模型&#xff0c;从…

828华为云征文|部署在线论坛网站 Flarum

828华为云征文&#xff5c;部署在线论坛网站 Flarum 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 Flarum3.1 Flarum 介绍3.2 Flarum 部署3.3 Flarum 使用 四、总结 一、…

针对考研的C语言学习(定制化快速掌握重点2)

1.C语言中字符与字符串的比较方法 在C语言中&#xff0c;单字符可以用进行比较也可以用 > , < ,但是字符串却不能用直接比较&#xff0c;需要用strcmp函数。 strcmp 函数的原型定义在 <string.h> 头文件中&#xff0c;其定义如下&#xff1a; int strcmp(const …

Vue.js组件开发指南

Vue.js组件开发指南 Vue.js 是一个渐进式的 JavaScript 框架&#xff0c;用于构建用户界面。它的核心是基于组件的开发模式。通过将页面分解为多个独立的、可复用的组件&#xff0c;开发者能够更轻松地构建复杂的应用。本文将深入探讨 Vue.js 组件开发的基础知识&#xff0c;并…

基于springoot新能源充电系统的设计与实现

新能源充电系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统新能源充电系统信息管理难度…

国产纯电SUV都在秀,只有Model Y在挨揍

文/王俣祺 导语&#xff1a;如果想知道纯电SUV应该怎么选&#xff0c;一定有人告诉你“无脑选Model Y”&#xff0c;虽说特斯拉确实粉丝多&#xff0c;但这也恰恰证明Model Y一度成为了纯电SUV的标杆。有标杆自然就有挑战者&#xff0c;随着阿维塔07、智己LS6以及乐道L60先后上…

云南省职业院校技能大赛赛项规程(软件测试)

赛项名称&#xff1a;软件测试 英文名称&#xff1a;Software Testing 赛项组别&#xff1a;高等职业教育 赛项编号&#xff1a;GZ034 目录 一、 赛项信息 二、竞赛目标 三、竞赛内容 1、本赛项考查的技术技能和涵盖的职业典型工作任务 2、专业核心能力与职业综合能力…

商标名称注册查询,到底是查询什么!

在商标注册前是需要商标名称注册查询&#xff0c;那这个到底是查询什么&#xff0c;普推知产商标老杨发现&#xff0c;近日国家知产局发布《商标代理委托合同示范文本》征求意见稿&#xff0c;虽然是参考使用不具有强制性&#xff0c;里面对商标名称注册查询描述是申请前商标检…

完成UI界面的绘制

绘制UI 接上文&#xff0c;在Order90Canvas下创建Image子物体&#xff0c;图片资源ui_fish_lv1&#xff0c;设置锚点&#xff08;CountdownPanelImg同理&#xff09;&#xff0c;命名为LvPanelImg,创建Text子物体&#xff0c;边框宽高各50&#xff0c; &#xff0c;重名为LvT…

阻焊层解析:PCB的“保护伞”是什么?

在电子制造行业中&#xff0c;尤其是PCBA贴片加工领域&#xff0c;阻焊层是一个重要的概念。以下是对阻焊层的详细讨论分析&#xff0c;包括其定义、作用以及类型。 阻焊层的定义 阻焊层&#xff0c;顾名思义&#xff0c;是一种用于阻止焊接的材料层。在PCB&#xff08;印刷电…

11.C++程序中的常用函数

我们将程序中反复执行的代码封装到一个代码块中&#xff0c;这个代码块就被称为函数&#xff0c;它类似于数学中的函数&#xff0c;在C程序中&#xff0c;有许多由编译器定义好的函数&#xff0c;供大家使用。下面就简单说一下&#xff0c;C中常用的函数。 1.sizeof sizeof函…

Perceptually Optimized Deep High-Dynamic-RangeImage Tone Mapping

Abstract 我们描述了一种深度高动态范围&#xff08;HDR&#xff09;图像色调映射算子&#xff0c;该算子计算效率高且感知优化。 我们首先将 HDR 图像分解为归一化拉普拉斯金字塔&#xff0c;并使用两个深度神经网络 (DNN) 根据归一化表示估计所需色调映射图像的拉普拉斯金字…

Mybatis缓存机制(图文并茂!)

目录 一级缓存 需求我们在一个测试中通过ID两次查询Monster表中的信息。 二级缓存 案例分许(和上述一样的需求) EhCache第三方缓存 在了解缓存机制之前&#xff0c;我们要先了解什么是缓存&#xff1a; ‌缓存是一种高速存储器&#xff0c;用于暂时存储访问频繁的数据&…

利用大模型改进知识图谱补全的研究

人工智能咨询培训老师叶梓 转载标明出处 尽管现有的基于描述的KGC方法已经利用预训练语言模型来学习实体和关系的文本表示&#xff0c;并取得了一定的成果&#xff0c;但这些方法的性能仍然受限于文本数据的质量和结构的不完整性。 为了克服这些限制&#xff0c;中国科学技术…

PG高可靠模拟

模拟延迟 主库故障&#xff0c;备库尝试切换为主库

9.29 LeetCode 3304、3300、3301

思路&#xff1a; ⭐进行无限次操作&#xff0c;但是 k 的取值小于 500 &#xff0c;所以当 word 的长度大于 500 时就可以停止操作进行取值了 如果字符为 ‘z’ &#xff0c;单独处理使其变为 ‘a’ 得到得到操作后的新字符串&#xff0c;和原字符串拼接 class Solution { …