【C++】踏上C++学习之旅(九):深入“类和对象“世界,掌握编程的黄金法则(四)(包含四大默认成员函数的练习以及const对象)

文章目录

  • 前言
  • 1. 实现Date类的构造函数
  • 2. 实现Date类的拷贝构造函数
  • 3. 实现Date类的赋值运算符重载
  • 4. 实现各Date对象之间的比较接口
  • 5. 实现Date对象的加减接口
  • 6. const成员
  • 7. 取地址及const取地址操作符重载

前言

在我们前面学习到了"类和对象"的四大默认成员函数(构造函数、析构函数、拷贝构造函数、赋值运算符重载),这四大默认成员函数也是我们在以后使用"类和对象"这块知识时经常遇到的。本章将会围绕着如何实现一个Date类,来让大家尽快学会编写和更加深刻理解关于"类"封装的思想在实际当中的应用!

本文会分板块逐一讲解,在文章的末尾放有本次实现Date类的全部源码。

哈哈哈

1. 实现Date类的构造函数

所谓的 “Date” 翻译过来就是 “日期” 的意思,那它的成员变量一定是年月日。那我们就可以这么实现Date类的构造函数。

class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:int _year; //年int _month; //月int _day; //日
};

这里我们写成全缺省的默认构造函数是十分有讲究的,一方面当我们先使用这个缺省值时,我们就直接有类实例化出对象就行,不需要显式的传递参数;另一方面,当我们想用自己的传递的参数,就直接显式传递函数即可。一举两得。

2. 实现Date类的拷贝构造函数

class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;
};

这里对于拷贝构造函数的思路没有太多的讲解,只需要大家注意一个点就是,形参一定是对于类类型的引用,避免引发无穷的递归调用。

3. 实现Date类的赋值运算符重载

class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}private:int _year;int _month;int _day;
};

关于赋值运算符重载的返回值,一定是*this,而不是this。因为我们是要返回这个待赋值对象的引用,而this是这个待赋值对象的指针类型为Date* const。还有的人会考虑生命周期的问题,其实这里大可不必担心,虽然this指针的生命周期在这个成员函数中,出了这个作用域就会被销毁,但是我们返回的不是this而是*this*this就是那个待拷贝的对象,所以其的生命周期是在main函数中的!

4. 实现各Date对象之间的比较接口

本次的是实现主要涉及到大小之间的比较,目的是锻炼大家对于运算符重载的用法。

//年份之间的比较大小
class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//年份之间的比较大小bool operator>(const Date& d){if (_year > d._year){return true;}else if (_year == d._year && _month > d._month){return true;}else if (_year == d._year && _month == d._month && _day > d._day){return true;}return false;}bool operator==(const Date& d){return _year == d._year && _month == d._month && _day == d._day;}bool operator!=(const Date& d){return !(*this == d);}bool operator>=(const Date& d){return (*this == d) || (*this > d);}bool operator<=(const Date& d){return (*this == d) || (*this < d);}bool operator<(const Date& d){return !(*this >= d);}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}private:int _year;int _month;int _day;
};

这里不仅体现了运算符重载,还体现出了代码复用的重要性!

5. 实现Date对象的加减接口

这里Date类对象的加减是指:年份与年份之间的相减,年份与天数之间的相减,年份与天数的相加。

class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date operator+(int day) const{Date tmp = *this;tmp._day += day;//我们还要考虑一下天数相加会导致的 月进位 甚至是 年进位//所以我们得获取每个月的天数while (tmp._day > tmp.GetMonthDay()){tmp._day -= tmp.GetMonthDay();tmp._month++;if (tmp._month > 12){tmp._year++;tmp._month = 1;}}return tmp;}Date& operator+=(int day){_day += day;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return *this;}Date& operator-=(int day){_day -= day;while (_day <= 0){--_month;if (_month < 1){_month = 12;--_year;}_day += GetMonthDay();}return *this;}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}Date& operator++(){_day += 1;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return *this;}Date operator++(int){Date tmp = *this;_day += 1;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return tmp;}private:int GetMonthDay(){int month_day[] = { 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;}return month_day[_month];}private:int _year;int _month;int _day;
};

这个可以很好的锻炼大家对于前置++和后置++的写法。

到这里Date类我们就全部实现完毕了,是不是很简单呢。一下就是Date类实现全部的源码:

class Date
{
public://全缺省的默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date operator+(int day) const{Date tmp = *this;tmp._day += day;//我们还要考虑一下天数相加会导致的 月进位 甚至是 年进位//所以我们得获取每个月的天数while (tmp._day > tmp.GetMonthDay()){tmp._day -= tmp.GetMonthDay();tmp._month++;if (tmp._month > 12){tmp._year++;tmp._month = 1;}}return tmp;}//年份之间的比较大小bool operator>(const Date& d){if (_year > d._year){return true;}else if (_year == d._year && _month > d._month){return true;}else if (_year == d._year && _month == d._month && _day > d._day){return true;}return false;}bool operator==(const Date& d){return _year == d._year && _month == d._month && _day == d._day;}bool operator!=(const Date& d){return !(*this == d);}bool operator>=(const Date& d){return (*this == d) || (*this > d);}bool operator<=(const Date& d){return (*this == d) || (*this < d);}bool operator<(const Date& d){return !(*this >= d);}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}Date& operator+=(int day){_day += day;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return *this;}Date& operator++(){_day += 1;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return *this;}Date operator++(int){Date tmp = *this;_day += 1;while (_day > GetMonthDay()){_day -= GetMonthDay();++_month;if (_month > 12){++_year;_month = 1;}}return tmp;}Date& operator-=(int day){_day -= day;while (_day <= 0){--_month;if (_month < 1){_month = 12;--_year;}_day += GetMonthDay();}return *this;}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}private:int GetMonthDay(){int month_day[] = { 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;}return month_day[_month];}private:int _year;int _month;int _day;
};

6. const成员

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

函数

#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
void Test()
{Date d1(2022, 1, 13);d1.Print();const Date d2(2022, 1, 13);d2.Print();
}int main()
{Test();return 0;
}

图片

请思考下面的几个问题:

  1. const对象可以调用非const成员函数吗?

不可以,因为权限被放大了。

  1. 非const对象可以调用const成员函数吗?

可以,因为权限被缩小了。

  1. const成员函数内可以调用其它的非const成员函数吗?

不可以,因为权限被放大了

  1. 非const成员函数内可以调用其它的const成员函数吗?

可以,因为权限被缩小了。

那这里我们就可以根据这个const成员进一步优化我们的Date类的实现!

7. 取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成

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

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容的地址
我们就可以改造上面的函数:

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

好了本文到这里就结束了。

如果觉得本文写的还不错的话,麻烦给偶点个赞吧!!!

哈哈哈

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

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

相关文章

项目中排查bug的思路案例

bug描述&#xff1a;调用了删除的接口&#xff0c;执行成功了&#xff0c;也删掉了选中的数据&#xff0c;但是不执行删除后的处理操作&#xff0c;会报一个“系统未知错误&#xff0c;请反馈给管理员” 解决&#xff1a; 成功删掉了数据&#xff0c;但删除后的操作没有执行&a…

Mysql数据库1——基本原理和基础操作

文章目录 Mysql数据库1——基本原理和基础操作1. 基本概念2. Mysql体系结构2.1 连接层2.2 服务层2.3 存储引擎层 3. 三级范式与反范式4. 完整性约束4.1 实体完整性约束4.2 参照完整性约束 5. CRUDDDLDMLDCLDQL 6. 高级查询基础查询条件查询分页查询查询结果排序分组聚合查询联表…

初学 flutter 问题记录

windows搭建flutter运行环境 一、运行 flutter doctor遇到的问题 Xcmdline-tools component is missingRun path/to/sdkmanager --install "cmdline-tools;latest"See https://developer.android.com/studio/command-line for more details.1&#xff09;cmdline-to…

【STM32】时钟系统

在我们学习STM32之前&#xff0c;我们需要先了解STM32系列芯片的时钟系统&#xff0c;这个是我们学习这个芯片的基础。为什么时钟系统这么重要呢&#xff1f;举个例子&#xff0c;如果把STM32比作我们的整个人体&#xff0c;那么时钟就是维持我们人体正常工作的心脏。STM32芯片…

Android Studio 设置不显示 build-tool 无法下载

2024版本查看build-tool版本 File -> Settings -> Languages & Frameworks -> Android SDK 或者直接打开Settings后搜索“SDK” 解决方案 将 Android Studio 升级到2022.2.1以上的版本将 C:/Windows/System32/drivers/etc/hosts 文件用管理员身份打开&#xff0c…

H.265流媒体播放器EasyPlayer.js H5流媒体播放器关于如何查看手机端的日志信息并保存下来

现今流媒体播放器的发展趋势将更加多元化和个性化。人工智能的应用将深入内容创作、用户体验优化等多个方面&#xff0c;带来前所未有的个性化体验。 EasyPlayer.js H.265流媒体播放器属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#…

Elasticsearch:如何部署文本嵌入模型并将其用于语义搜索

你可以按照这些说明在 Elasticsearch 中部署文本嵌入模型&#xff0c;测试模型并将其添加到推理提取管道。它使你能够生成文本的向量表示并对生成的向量执行向量相似性搜索。示例中使用的模型在 HuggingFace上公开可用。 该示例使用来自 MS MARCO Passage Ranking Task 的公共…

使用Python生成F分布表并导出为Excel文件

使用Python生成F分布表并导出为Excel文件 一、引言二、准备工作三、代码实现四、运行结果五、总结六、参考资料 一、引言 在统计分析中&#xff0c; F F F分布是一种非常重要的连续概率分布&#xff0c;广泛应用于方差分析、回归分析的显著性检验等场景。为了方便查阅和使用F分…

RNN公式解释:实现记忆功能;RNN的状态向量

目录 RNN公式解释:实现记忆功能 一、词向量 二、RNN的状态向量 三、词向量变为状态向量的过程 四、总结 RNN公式解释:实现记忆功能 在RNN(递归神经网络)中,词向量变为状态向量的过程,实际上是RNN处理时序数据的一个核心环节。以下是对这一过程的详细解释: 一、词向…

激光雷达定位初始化的另外一个方案 通过键盘按键移动当前位姿 (附python代码)

通常使用的是通过在 rviz 中点选指定初始化位置和方向来完成点云的初始化匹配。 但是这种粗略的初始化方法有时候可能不成功,因此需要使用准确的初始化方法,以更好的初始值进行无损检测配准。 为了提供更好的匹配初始值,我使用 Python 脚本获取键盘输入,并不断调整这个匹配…

小试牛刀-Anchor安装和基础测试

目录 一、编写目的 二、安装步骤 2.1 安装Rust 设置rustup镜像 安装Rust 2.2 安装node.js 2.3 安装Solana-CLI 2.4 安装Anchor CLI 三、Program测试 四、可能出现的问题 Welcome to Code Blocks blog 本篇文章主要介绍了 [Anchor安装和基础测试] 博主广交技术好友&…

自存 sql常见语句和实际应用

关于连表 查询两个表 SELECT * FROM study_article JOIN study_article_review 查询的就是两个表相乘&#xff0c;结果为两个表的笛卡尔积 相这样 这种并不是我们想要的结果 通常会添加一些查询条件 SELECT * FROM study_articleJOIN study_article_review ON study_art…

Linux-第2集-打包压缩 zip、tar WindowsLinux互传

欢迎来到Linux第2集&#xff0c;这一集我会非常详细的说明如何在Linux上进行打包压缩操作&#xff0c;以及解压解包 还有最最重要的压缩包的网络传输 毕竟打包压缩不是目的&#xff0c;把文件最终传到指定位置才是目的 由于打包压缩分开讲没有意义&#xff0c;并且它们俩本来…

关于pip install 包 时出现This is an issue with the package mentioned above,not pip的问题

关于This is an issue with the package mentioned above,not pip 今天在用pip下载gensim包的时候&#xff0c;出现了上图中的问题&#xff0c;提示信息是&#xff1a;This is an issue with the package mentioned above,not pip 那说明是包的问题&#xff0c;而不是在使用 pi…

高阶云服务-ELB+AS

ELBAS 弹性负载均衡弹性伸缩 原来1台web服务器不满足相应&#xff0c;现部署多台提供相同服务&#xff1b; 由于多个服务器多个ip该如何提供给应用呢&#xff1f; 引申出负载均衡&#xff08;HAProxy&#xff0c;LVS01四层&#xff0c;Nginx七层&#xff09; 防单点故障做主备…

【论文阅读】WaDec: Decompiling WebAssembly Using Large Language Model

论文阅读笔记:WaDec: Decompiling WebAssembly Using Large Language Model 1. 来源出处 论文标题: WaDec: Decompiling WebAssembly Using Large Language Model作者: Xinyu She, Yanjie Zhao, Haoyu Wang会议: 39th IEEE/ACM International Conference on Automated Softwar…

oracle导入线上数据的全步骤

多租户架构允许oracle数据库成为一个多租户的容器数据库&#xff0c;也就是CDB&#xff0c;container database&#xff0c;与之相对应的&#xff0c;则是插入到这个容器里面的可插拔式数据库&#xff0c;pluggable database 一个CDB可以包含0&#xff0c;1或者多个用户创建的…

vue中el-select 模糊查询下拉两种方式

第一种&#xff1a;先获取所有下拉数据再模糊查询&#xff0c;效果如下 1&#xff0c;页面代码&#xff1a;speciesList是种类列表List, speciesId 是speciesList里面对应的id&#xff0c;filterable是过滤查询标签 <el-form-item label"种类" prop"species…

【Linux从青铜到王者】详解ip协议(待完善)

前言 之前我们讲的udp和tcp协议&#xff0c;是处于传输层的协议 而ip协议&#xff0c;是处于传输层下面的网络层的协议 一个报文传输的时候&#xff0c;表面上是一个主机的传输层运输到另一个主机的传输层&#xff0c;本质是其实是由应用层不断向下交付到数据链路层&#xff…

pytorch奇怪错误

ValueError: At least one stride in the given numpy array is negative, and tensors with negative strides are not currently supported. (You can probably work around this by making a copy of your array with array.copy().) 今天在这里遇到了一个奇怪的bug impor…