【C++】————类和对象(下)

 9efbcbc3d25747719da38c01b3fa9b4f.gif

                                                      作者主页:     作者主页

                                                      本篇博客专栏:C++

                                                      创作时间 :2024年6月25日

9efbcbc3d25747719da38c01b3fa9b4f.gif

一、日期类

首先我们先来看一下通过类实现对日期的一系列处理,同时给大家说一下当中存在的一些细节问题:

1.1 GetMonthDay函数

这个函数的作用就是我们输入一个得到某一年某个月的天数,对后续的一些函数有着非常重要的作用,但我们要记得一个特殊情况,那就是闰年,因为闰年的二月是29天,非闰年是28天,注意这种情况就可以写代码了。

// 获取某年某月的天数
int GetMonthDay(int year, int month)
{//日期数组int MonthDayarr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//判断闰年if (2 == month && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)){return MonthDayarr[2] + 1;}else{return MonthDayarr[month];}
}

这里我们要注意的点就是先判断是不是二月,不是二月我们就没有必要去判断是否是闰年了,没必要了就,这样会节省很多时间。

1.2日期类中的两种构造函数

全缺省构造:

这里我们要给大家讲两种类型的构造函数,一种是全缺省构造函数,另一种是拷贝构造函数,这两种函数不清楚是啥的话可以去看这篇博客:构造函数和全缺省构造函数

我怕们先来说全缺省构造函数:

// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1)
{_year = year;_month = month;_day = day;//检查日期是否合法if (!(year >= 1&& (month >= 1 && month <= 12)&& (day >= 1 && day <= GetMonthDay(year, month)))){cout << "日期非法" << endl;}
}

这就是全缺省构造函数,如果传过来值,就赋值,否则就用默认给定的值,在平常的写代码过程中,我还是建议大家去写这种构造函数,因为这种构造函数满足的场景更加多样,不传值也可以,传值当然也可以。

Date d1(2024, 6, 23);
Date d2;
Date d3(2024, 12);

像这样的传值方式都是可以的 

 拷贝构造:

接下来看一下拷贝构造函数:

// 拷贝构造函数
// d2(d1)
Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}

这种就是我们的拷贝构造函数,其实就是传一个对象的别名,然后将这个对象的值赋给另一个对象,这就叫拷贝构造。

使用时可以这样去赋值:

Date d1(2024, 6, 23);
Date d2(d1);

1.3比较两个日期是否相等

比较两个日期是否相等的这种函数被称为赋值运算符重载,像这种函数的函数名,我们应该怎样去写呢,可能有些同学直接这样去写:

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

但是其实这样会直接报错,我们来看一下编译器给出的错误原因:

这就是这里报错的原因,我们回想一下,是不是忽略了一个叫this指针的东西,没错,我们这里去调这个函数的时候,会有一个隐藏的this指针,所以我们应该这样去写这个函数:

// ==运算符重载
bool operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}

这样写才符合要求

调用时是这样调用;

int ret=d1==d2;
int ret=d1.operator==(d2);

1.3赋值运算符重载

赋值运算符重载就是用来为对象赋值的:

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

这就是赋值运算符重载。

调用时可以这样:

Date d1(2024,10,1);
Date d2;
d2.operator=(d1);

1.4>和<运算符重载

这里是比较两个日期的大小,我们肯定先去比较年,其次是月,最后是日,按照规则来就好了

bool operator>(const Date& d)
{if (_year > d._year){return true;}else if (_year == d._year){if (_month > d._month){return true;}else if (_month == d._month){if (_day == d._day){return true;}}}return false;
}// >=运算符重载
bool operator>=(const Date& d)
{return operator>(d) || operator==(d);
}// <运算符重载
bool operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}

这里我直接把两个写在一起,哪里不懂可以问我

1.5日期+天数与日期+=天数

这里我先来说一下这两个的区别,其实大致相同,不同的就是:

日期+=天数是改变了传过来的日期,在返回,而日期+天数并没有改变原来的日期,

看一下代码:

// 日期+=天数
Date& operator+=(int day)
{//如果传过来一个负数if (day < 0){return *this -= -day;}_day += day; //_day加上要加的天数while (_day > GetMonthDay(_year, _month)) //加完后,如果_day大于当月天数,进入循环{//_day减去当月天数,_month++_day -= GetMonthDay(_year, _month);_month++;if (_month > 12)//如果_month大于12,则_year++{_month = 1;_year++;}}return *this;
}// 日期+天数
Date operator+(int day)
{Date ret(*this);ret += day;//这里直接调用上面这个函数就可以了return ret;}

可以发现日期加天数就是创建一个临时的对象用来储存传过来的对象,然后返回,起到不改变原来对象的作用。

1.6日期-天数与日期-=天数

这个与上面相同,我不做过多的介绍,直接上代码:

// 日期-=天数
Date& operator-=(int day)
{_day -= day;while (_day <= 0){_month--;if (_month < 1){_month = 12;_year--;}_day += GetMonthDay(_year, _month);}return *this;
}
// 日期-天数
Date operator-(int day)
{Date ret(*this);ret -= day;return ret;
}
// 前置++
Date& operator++()
{*this += 1;return *this;
}

1.7前置++与后置++

这里我们如果通过函数调用来区分前置++和后置++呢,这个函数如何去写呢,这里我们有一个很妙的写法,就是前置加加正常写,然后后置加加里面加入一个int,构成重载函数:

// 前置++
Date& operator++()
{*this += 1;return *this;
}
// 后置++
Date operator++(int)//用int来构成重载函数,区分前置++和后置++
{//cout << "后置++" << endl;Date tmp(*this);*this += 1;return tmp;
}

传值时想要后置加加里面加上一个整形的数即可,0,1都可以

1.8前置--与后置--

还是如上,直接上代码:

// 前置--
Date& operator--()
{*this -= 1;return *this;
}
// 后置--
Date operator--(int)
{//cout << "后置--" << endl;Date tmp(*this);*this -= 1;return tmp;
}

二、const对象的调用

我们知道,我们调用一个对象的时候,会传递一个this指针来,传过去的this指针是这种类型的:Date* const this,也就是说明这里的this指向的对象不能改变,但是本身的值可以改变,但是如果我们传一个const对象,就是指向的对象和值都不能改变,那样会咋变呢,如果直接传,就会出现权限的放大问题,我们知道,权限可以缩小,但绝对不可以放大。

这时候我们可以这样去做,在函数定义和对象的声明的后面加上一个const:

像这样。

在 C++中,要调用一个const对象,可以使用const引用或const指针。const引用或const指针可以绑定到const对象,从而避免对const对象的直接修改。

 

避免权限放大的方法是在调用const对象时,使用const引用或const指针。这样可以确保在函数内部不会修改const对象的值,从而避免权限放大的问题。

三、初始化列表

向我们之前讲的构造函数,以及拷贝构造函数,赋值构造函数等等,都是为了给类中的内置成员进行初始化的,但是如果出现了下面的这几种情况呢?

1、引用

2、const

3、没有默认构造自定义类型成员(必须显示传参调构造)

我们知道,引用只能在定义的时候初始化,然后const定义的变量也无法被修改自定义类型要调用它的默认构造函数, 如果没有默认构造函数就会error

所以此时C++中就给出了一种合适的解决方法,那就是初始化列表。

	MyQueue(int n, int& rr):_pushst(n), _popst(n),_x(1),_y(rr){_size = 0;}/*{_size(0);}*/
private:Stack _pushst;Stack _popst;int _size;//这两个必须在定义时初始化const int _x;int& _y;
};

就像这样,这就是初始化列表的写法,在{}上面进行操作,但是不管{}有没有值,必须带着{}才可以。写法就是:然后后面每一句前面几上,即可。不要去在乎这样写的原因是啥,主要还是要记住就是这样去写。

这里还有一点要注意,初始化列表初始化的顺序是声明的顺序,与初始化列表的顺序无关:

class A
{
public://初始化列表初始化的顺序是生命的顺序,不是初始化的顺序A(int a):_a1(a), _a2(_a1){}void Print(){cout << _a1 << " " << _a2 << endl;}private:int _a2;int _a1;
};

如果运行这一串代码,如果传过去一个1你认为结果是啥,结果其实应该是一个随机值加上1,因为声明的顺序是_a2在前面。然后_a1在后面,所以初始化_a2,所以是一个随机值。

四、static成员

我们知道static的作用是:

  1. 静态局部变量:在函数内定义的静态局部变量,在程序执行期间只初始化一次,并且其生命周期贯穿整个程序运行过程,而不是仅在函数调用期间存在。
  2. 静态成员变量:属于整个类而不是某个具体对象,所有该类的对象共享这一个静态成员变量。
  3. 静态成员函数:可以通过类名直接调用,不依赖于具体对象,不能访问非静态成员变量(但可以通过对象访问)。
  4. 静态全局变量:限制了该变量的作用域仅在当前文件内可见,避免了命名冲突。

这里我们要说的是其实static也可以在类中声明,被定义的成员被称为类的静态成员,我们知道静态成员不是谁所特有的,而是共享的,不属于某个具体的类,存放在静态区

即使声明在类中,我们依然要在外面定义:

类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

我们可以根据这个特点去定义它

class A
{
public:A() { ++_sount; }A(const A& t) { ++_sount; }~A() { //--_sount;}//静态成员函数//没有this指针,只能访问静态成员,无法访问其他的成员,访问其他成员变量要依靠this指针static int GetCount(){return _sount;}
private:int _a1 = 1;int _a2 = 2;//静态区,不存在于对象中//这里不能给缺省值,因为缺省值是给初始化列表的,// 但是这里不会走初始化列表,因为他在静态区,所以不走初始化列表//价值:属于所有整个类,属于所有对象static int _sount;
};//这是_sount的定义
int A::_sount = 0;

这里我们还需要注意一点就是静态成员函数无法调用非静态成员函数的,因为静态成员函数没有this指针的传递。

五、友元函数

友元是一种很有效的方式,但是并不推荐使用,因为它会破坏封装

比如说
Date d1;
cout << d1 << endl;
要实现这样的代码, 就只能重载 <<

如果实现成Date的成员函数, ostream& operator<<(ostream& _cout)
第一个参数是this, 所以调用的时候就成了 d1 << cout ;
这样特别奇怪,所以一般这个都会重载成全局函数,所以又引发新的问题, 类的成员变量一般是私有的
如果重载成全局函数,无法访问私有/保护成员,所以友元就派上用场了

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在
类的内部声明,声明时需要加friend关键字

像这样:

这样就可以访问类中私有成员

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

六、友元类

友元类其实就是两个类之间的友元:

class A
{//声明 B是A的友元//此时B就可以访问A中的私有,但是不证明A可以访问B的私有//也就是A把B当成朋友了,但是不能说明B把A当成朋友friend class B;};class B
{};

像这样,此时B就是A的友元,B就可以访问A中的私有成员,但是A不可以访问B的私有,

也就是A把B当成朋友了,但是不能说明B把A当成朋友

七、内部类

内部类其实就是在一个类中再去定义一个类:

内部类,内部类是外部类的私有
在一个类中,除了定义函数和变量,还可以定义类,就叫内部类,类似于套娃
class A
{
private:static int k;int h;
public:void func(){cout << "func" << endl;}private://内部类//放到A里面//仅仅受到类域限制class B//这里的B天生就是A的友元。{public:B(int b = 1){_b = b;}void foo(const A& a){//cout << k << endl;//cout << a.h << endl;}private:int _b;};
};

这就是一个内部类,然后这里的B天生就是A的友元。

 八、拷贝对象的优化

编译器对拷贝构造的优化通常有以下几种方式:

  • 内联优化:编译器将拷贝构造函数的代码直接插入到调用处,从而避免了函数调用的开销。
  • 指针传递优化:如果传递对象的时候使用指针或引用,而不是拷贝整个对象,可以避免大量的内存拷贝操作,从而提高程序的执行效率。
  • 移动语义优化:如果拷贝对象的目的是为了将其传递给另一个函数或对象,编译器可以使用移动语义来避免不必要的拷贝操作。移动语义是一种新的语言特性,在 C++11 标准中引入,可以将对象的资源所有权从一个对象转移到另一个对象,避免了拷贝数据的开销。
  • 返回值优化(RVO):如果函数返回一个对象,编译器可以将这个对象在函数内部创建,然后直接返回给调用者,从而避免了拷贝对象的开销。这种优化方式被称为 RVO。

我给大家看一个例子:

#include<iostream>using namespace std;class A
{
public:A(){cout << "A()" << endl;}A(const A& a){cout << "A(const A& a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};A func()
{A a;return a;
}int main()
{A tmp=func();return 0;
}

大家认为这个例子共进行了多少次的拷贝构造和析构?

所以是两次构造,一次拷贝构造,一次赋值重载,三次析构。

如果我们换个方式接收呢

func改成这样:

A func()
{
return A();
}
A
int main()
{
A tmp=func();return 0;
}

我们再分析一下,A()是一次构造,返回是一次拷贝构造,然后拷贝构造给tmp,然后析构一次临时对象,西沟一次匿名对象,析一次tmp。

所以就是一次构造,两次拷贝构造,三次析构。可是运行一下就发现:

是一次构造一次析构。

为什么呢?

原因就是使用A()去返回的时候还返回啥临时对象啊,直接在接受返回值的地方构造就可以了,一个表达式,多次构造 +拷贝构造的 都会被优化为1次构造,这就是编译器的优化。

九、匿名对象

最后再来简单来说一下匿名对象,其实就是创建一个对象时不给他名字,

  • 有名对象的生命周期是当前作用域(main函数)
  • 匿名对象的作用域是当前这一行,即用即销毁

最后:

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识:
屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。
正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

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

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

相关文章

客户有哪些封装案例,一句克服使用让PCBA工厂泪流满面

作者 | 高速先生成员--王辉东 天空下着雨&#xff0c;萧萧从窗前经过&#xff0c;看窗里。 翠萍那娇艳欲滴的脸上挂着两串泪滴。 萧萧一进去&#xff0c;问啥情况。 翠萍往电脑屏幕一指。 当萧萧看向屏幕一瞬间。 那些曾经以为早已遗忘的伤痛&#xff0c;会在某些时刻如潮…

新增题目接口开发

文章目录 1.基本设计2.生成CRUD代码1.生成五张表的代码1.subject_info2.subject_brief3.subject_judge4.subject_multiple5.subject_radio 2.将所有的dao放到mapper文件夹3.将所有实体类使用lombok简化4.删除所有mapper的Param("pageable") Pageable pageable5.删除所…

【大数据】—谁是世界上最富的人?

引言 在2024年&#xff0c;全球财富的分布再次成为公众和经济学家关注的焦点。随着经济的波动和新兴市场的崛起&#xff0c;亿万富翁的名单也在不断变化。本文将深入探讨这一现象&#xff0c;通过最新的数据可视化分析&#xff0c;揭示世界上最富有的人在2024年的财富状况和趋…

解锁品牌推广小妙招:如何高效塑造品牌影响力

在信息化大时代&#xff0c;企业如何做好品牌传播是一个复杂而重要的课题。随着信息爆炸和新媒体的兴起&#xff0c;传统的广告投放已经无法完全满足品牌的宣传需求&#xff0c;媒体公关传播越来越为企业所重视。今天投媒网就来与您分享在信息化大时代&#xff0c;企业如何做好…

在Mandelbrot 集中“缩放”特定区域

1、问题背景 在创建一个快速生成 Mandelbrot 集图像的 Python 程序时&#xff0c;程序开发者遇到一个问题&#xff1a;他想要渲染该集合的一个特定区域&#xff0c;但他不知道如何修改代码中的数学部分来实现 “缩放”。 2、解决方案 第一种解决方案 问题根源是代码中的一行…

SVN学习(007 svn安装Tortoise工具)

尚硅谷SVN高级教程(svn操作详解) 总时长 4:53:00 共72P 此文章包含第58p-第p72的内容 介绍 安装 选择自己想要装软件的文件夹 进入工作目录&#xff0c;发现无svn的图标&#xff0c;重启电脑即可 就能看到svn的图标 settings功能 进行图标的查看 修改subversion配置文件 …

安卓直装植物大战僵尸杂交版V2.1版完美运行

在移动游戏的世界里&#xff0c;植物大战僵尸无疑是一款深受玩家喜爱的经典游戏。如今&#xff0c;随着技术的发展和玩家需求的变化&#xff0c;植物大战僵尸杂交版V2.1版应运而生&#xff0c;为安卓用户带来了全新的游戏体验。 这一全新版本在原有游戏的基础上进行了多项创新…

SAP系统中的应付账款(与MM集成,关账操作)

1. 与物料管理的集成 Plant: 工厂是后勤中的位于中心位置的组织对象&#xff0c;一个“工厂”可以是公司内的一个作业区&#xff0c;或一个分支机构。一个“工厂”可以是一个中央交付仓库&#xff0c;可以是一个区域的销售营业部&#xff0c;一个制造工厂&#xff0c;一个集团…

合适的智能猫砂盆到底怎么挑?开放式封闭式一次说清!

想当初我也是在网上看了各种测评&#xff0c;纠结了好久才下定决心入手了智能猫砂盆。封闭式和开放式都用过&#xff0c;各有各的利与弊&#xff0c;不过最后的我还是选择了开放式的智能猫砂盆&#xff0c;因为开放式的设计结构会更加方便我观察小猫&#xff0c;哪个铲屎官不喜…

采购OLED透明屏指南

一、引言 OLED透明屏作为一种前沿的显示技术&#xff0c;以其独特的透明度和出色的显示效果&#xff0c;受到了众多行业的青睐。在采购OLED透明屏时&#xff0c;需要综合考虑多个因素&#xff0c;以确保选择到符合需求的高质量产品。以下是一份详细的采购OLED透明屏指南&#x…

《云南化工》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《云南化工》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定学术期刊。 问&#xff1a;《云南化工》级别&#xff1f; 答&#xff1a;省级。主办单位&#xff1a;云天化集团有限责任公司 主管单位&#xff1a;…

让我们聊聊网络安全中会涉及到的IP地址(IP协议)、MAC地址、路由、DNS协议(域名系统)、NAT技术(协议)、以太网帧、ARP协议

网络安全中会涉及到的IP地址&#xff08;IP协议&#xff09;、MAC地址、路由、DNS协议&#xff08;域名系统&#xff09;、NAT技术&#xff08;协议&#xff09;、以太网帧、ARP协议 一.IP地址&#xff08;IP协议&#xff09;1.IP地址&#xff08;IP协议&#xff09;的作用2.IP…

Go 实现SFTP连接服务

我们将SFTP连接和处理逻辑&#xff0c;以及登录账户信息封装&#xff0c;这样可以在不同的地方重用代码&#xff0c;并且可以轻松地更改登录凭据。下面我将演示如何使用Go语言中的结构体来封装这些信息&#xff0c;并实现一个简单的SFTP服务器&#xff1a; package mainimport…

大厂薪资福利篇第五弹:小红书

欢迎来到绝命Coding&#xff01; 今天继续更新大家最关心的 大厂薪资福利系列&#xff01; 为什么计算机学子对大厂趋之若鹜呢&#xff1f;最直接的原因就是高薪资的吸引力。 • 但是薪资可不是简单的数字哦&#xff0c;里面还是有很多“学问”的。 • 很多同学对大厂薪资只有一…

《黑神话悟空》电脑配置要求

《黑神话&#xff1a;悟空》这款国内优秀的3A游戏大作&#xff0c;拥有顶级的特效与故事剧情&#xff0c;自公布以来便备受玩家期待&#xff0c;其精美的画面与流畅的战斗体验&#xff0c;对玩家的电脑配置提出一定要求。那么这款优秀的游戏需要什么样的电脑配置&#xff0c;才…

老板舍不得买库存管理软件❓一招解决

在当今快节奏的商业环境中&#xff0c;仓库管理是企业运作中不可或缺的一环。对于许多中小型企业而言&#xff0c;简易且高效的库存管理系统尤为重要。搭贝简易库存管理系统针对仓库的出入库进行有效管理&#xff0c;帮助企业实现库存的透明化和流程的自动化。 客户的痛点 1. …

基于SSM构建的校园失眠与压力管理系统的设计与实现【附源码】

毕业设计(论文) 题目&#xff1a;基于SSM构建的校园失眠与压力管理系统的设计与实现 二级学院&#xff1a; 专业(方向)&#xff1a; 班 级&#xff1a; 学 生&#xff1a; 指导教师&a…

SNEC天合储能秀:全球首发多元场景一站式工商业储能融合解决方案

6月13日-15日&#xff0c;SNEC2024光伏与智慧能源展在上海隆重举行&#xff0c;来自全球95个国家和地区3000家国内外展商齐聚展会&#xff0c;5000行业专家共话产业发展。致力于成为全球光储智慧能源解决方案的领导者&#xff0c;天合光能&#xff08;展位号&#xff1a;7.2H-E…

GitLab 不小心提交了master/develop版本如何回退

1. 找寻最近的版本&#xff0c;使用git reset --hard 回退到具体的提交版本号 2. git push origin master --force 这个会遇到gitlab默认拦截&#xff0c;处理版本 版本仓库页面&#xff0c;选择Setting——Repository&#xff0c;找到Protected branches 3. 再回到master分支…

Linux系统SPI子系统框架驱动调用实现详解

大家好,今天主要和大家分享一下,如何使用Linux系统中SPI子系统框架,也是分为主机驱动和设备驱动,裸机部分控制的是SPI控制器驱动,可以直接操控。 第一:Linux系统SPI主机驱动 SPI主机驱动就是SOC的SPI控制器驱动。Linux内核使用spi_master表示主机SPI驱动spi_master 是个结…