C++初阶(六)类和对象

news2024/7/6 17:53:39

文章目录

  • 一、 拷贝构造函数
    • 1、概念
    • 2、特性
  • 二、 赋值运算符重载
    • 1、运算符重载
    • 2、赋值运算符重载
      • 1、赋值运算符重载格式
      • 2、 赋值运算符只能重载成类的成员函数不能重载成全局函数
      • 3、 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝
    • 3、前置++和后置++重载
  • 三、日期类的实现
    • 1、代码实现
    • 2、细节讲解


一、 拷贝构造函数

1、概念

在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
在这里插入图片描述

那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。


2、特性

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

对比以上三张照片,我们可以更好的理解拷贝构造函数的特性一,特性二。

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    在这里插入图片描述
    注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

  2. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 10)
 {
 _array = (DataType*)malloc(capacity * sizeof(DataType));
 if (nullptr == _array)
 {
 perror("malloc申请空间失败");
 return;
 }
 _size = 0;
 _capacity = capacity;
 }
 void Push(const DataType& data)
 {
 // CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 ~Stack()
 {
 if (_array)
 {
 free(_array);
 _array = nullptr;
 _capacity = 0;
 _size = 0;
 }
 }
private:
 DataType *_array;
 size_t _size;
 size_t _capacity;
};
int main()
{
 Stack s1;
 s1.Push(1);
 s1.Push(2);
 s1.Push(3);
 s1.Push(4);
 Stack s2(s1);
 return 0;
}

在这里插入图片描述
以上代码会产生崩溃,崩溃的原因是什么的呢?
是因为它拷贝构造的时候,析构了两次,导致第二次析构时,空间没有了,产生了崩溃。
在这里插入图片描述

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

5.拷贝构造函数典型调用场景:

使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象
在这里插入图片描述

在这里插入图片描述
注意:为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。


二、 赋值运算符重载

1、运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

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

在这里插入图片描述
在这里插入图片描述

由上面的两张图,我们可以看出,只有成员变量为公有的时候才能运行,但我们为了保护成员变量我们该怎么做的,一种是,使用友元(这个以后会讲),这里我们说第二种方法,将函数引入类中。

在这里插入图片描述
但为什么我们引入类中还有出现错误呢,原来是这么有个隐藏参数this,我们不能忽略它的存在,所以我们该这样修改:
在这里插入图片描述


2、赋值运算符重载

1、赋值运算符重载格式

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

在这里插入图片描述

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

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

在这里插入图片描述

3、 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝

注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Time
{
public:
 Time()
 {
 _hour = 1;
 _minute = 1;
 _second = 1;
 }
 Time& operator=(const Time& t)
 {
 if (this != &t)
 {
 _hour = t._hour;
 _minute = t._minute;
 _second = t._second;
 }
 return *this;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d1;
 Date d2;
 d1 = d2;
 return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 10)
 {
 _array = (DataType*)malloc(capacity * sizeof(DataType));
 if (nullptr == _array)
 {
 perror("malloc申请空间失败");
 return;
 }
 _size = 0;
 _capacity = capacity;
 }
 void Push(const DataType& data)
 {
 // CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 ~Stack()
 {
 if (_array)
 {
 free(_array);
 _array = nullptr;
 _capacity = 0;
 _size = 0;
 }
 }
private:
 DataType *_array;
 size_t _size;
 size_t _capacity;
};
int main()
{
 Stack s1;
 s1.Push(1);
 s1.Push(2);
 s1.Push(3);
 s1.Push(4);
 Stack s2;
 s2 = s1;
 return 0;
}

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
在这里插入图片描述

3、前置++和后置++重载

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // 前置++:返回+1之后的结果
 // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
 Date& operator++()
 {
 _day += 1;
 return *this;
 }
 // 后置++:
 // 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
 // C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
 // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
 //       而temp是临时对象,因此只能以值的方式返回,不能返回引用
 Date operator++(int)
 {
 Date temp(*this);
 _day += 1;
 return temp;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d;
 Date d1(2022, 1, 13);
 d = d1++;    // d: 2022,1,13   d1:2022,1,14
 d = ++d1;    // d: 2022,1,15   d1:2022,1,15
 return 0;
}

三、日期类的实现

1、代码实现

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
    // d2(d1)
	Date(const Date& d);

	// 赋值运算符重载
 // d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d);
	// 析构函数
	~Date();
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-天数
	Date operator-(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 前置++
	Date& operator++();
	// 后置++
	Date operator++(int);
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();

	// >运算符重载
	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);
	// 日期-日期 返回天数
	int operator-(const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、细节讲解

细节1

在求 >= 、>、==、<、<=、!=等一系列操作时,只需要求出<和 = =或者>和= =即可,剩余的可以用一系列运算符进行操作。

细节二

在计算日期类相减的时候,记得要看d1和d2的大小,来计算结果的正负。

细节三

在计算日期类加天数的时候,要看天数的正负,然后进行正确操作。

细节四

如果返回值变量出了栈还存在,最好使用引用返回,性能高。

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

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

相关文章

[ROS系列]ORB_SLAM3错误版本(仅记录)

背景&#xff1a; 1、设备&#xff1a;pc&#xff1b;旭日派x3&#xff08;后续会加上&#xff0c;目前只有pc&#xff09; 2、环境&#xff1a;Ubuntu20.04&#xff1b;ROS2&#xff08;Foxy&#xff09;​​​​​​​ ros2机器人foxy版用笔记本摄像头跑单目orb_slam3-CSD…

142.环形链表

环形链表问题是链表中的经典问题&#xff0c;接下来是142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 的描述和详解 。 题目描述&#xff1a; 题目分析&#xff1a; situation1:当链表没有环形结构时&#xff0c;返回空&#xff1a; situation2&#xff1a;当链…

【html】图片多矩形框裁剪

说明 由于项目中需要对一个图片进行多选择框进行裁剪&#xff0c;所以特写当前的示例代码。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><base href"/"><title>图片裁剪</tit…

【广州华锐互动】智能家居设计3D虚拟还原系统

随着科技的飞速发展&#xff0c;人们对家居生活的需求也在不断提高。智能家居作为一种新兴的生活方式&#xff0c;正逐渐成为现代人追求的理想居住环境。而智能家居设计3D虚拟还原系统&#xff0c;正是为了让人们更好地了解和体验智能家居带来的便捷与舒适&#xff0c;让未来生…

Latex笔记

1. Section 编号方式(数字、字母、罗马)&计数器计数形式修改 IEEE模板中Section的编号是罗马数字&#xff0c;要是改投其他刊物的话可能得用阿拉伯数字&#xff0c;所以可以在导言部分做如下修改&#xff08;放在导言区宏包调用之后&#xff09;&#xff1a; \renewcomma…

AMD大规模裁员15%? 赔偿N+7?官方回应来了 | 百能云芯

近日&#xff0c;美国芯片大厂AMD遭传出在中国进行大规模裁员15%的消息&#xff0c;引发网友关注。 对此&#xff0c;AMD官方26日回应称&#xff0c;「网络传闻失实」&#xff0c;该公司并没有进行大规模裁员。 相反地&#xff0c;AMD是进行的「小幅度」的组织架构优化和重组&a…

使用node创建Web服务

创建Web服务 基于 Node.js 环境&#xff0c;使用内置 http 模块&#xff0c;创建 Web 服务程序 需求&#xff1a;引入 http 模块&#xff0c;使用相关语法&#xff0c;创建 Web 服务程序&#xff0c;响应返回给请求方一句提示 ‘hello&#xff0c;world’ 步骤&#xff1a; 引…

高速电路设计----第三章(3)LVPECL、CML逻辑电平详解

一、LVPECL介绍 LVPECL是ECL电平的正电源、低电压版本。 ECL电平是指发射极耦合逻辑&#xff08;Emitter Coupled Logic&#xff09;,与TTL相同&#xff0c;ECL的主体结构由三极管组成&#xff0c;不同的是 ECL内部的三极管工作在非饱和状态&#xff08;即截止或者放大状态&…

你能用原来的塞子保存你未喝完的葡萄酒吗?

这是一个工作日的晚上&#xff0c;你发现自己在商店的过道里寻找一瓶葡萄酒。在您的高贵探索中&#xff0c;您可能已经注意到了装饰在每个独特瓶子顶部的各种瓶盖&#xff0c;有些是软木塞&#xff0c;有些是一个简单的螺旋盖。在最终决定了一个有趣的外观黑皮诺葡萄酒带回家品…

python安装包失败 安装scipy包

最近在配深度学习环境&#xff0c;安装好了conda、CUDA、cuDNN&#xff0c;运行之后报错&#xff0c;没有scipy包。 试了很多方法&#xff1a; 用命令行pip、conda install 在pycharm下载包 在环境中添加包 都失败了 要么直接ERROR&#xff0c;要么超时 问了师兄 需要在conda环…

sys.dm_exec_requests中statement_start_offset与statement_end_offset

文章目录 1.缘起2.根因3.示例4.附录 1.缘起 mssql中查早阻塞与及其相关联的sql时&#xff0c;遇到如下内容&#xff0c;故记录一下&#xff0c; substring(dest_blocked.text,der.statement_start_offset/21,(case when der.statement_end_offset-1 then DATALENGTH(der.sta…

GZ035 5G组网与运维赛题第1套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项(高职组) 赛题第1套 赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通(35分) 子任务1:5G公共网络部署与调试(15分) 子任务2:5G室内与室外站点建设(20分) 竞赛模块2--5G公共网络运维与优化(3…

【ETL工具】本地环境IDEA远程DEBUG调试Flume代码

&#x1f984; 个人主页——&#x1f390;个人主页 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; 感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01;&…

推开科研成果落地“最后一扇门”

科研成果只有落地了&#xff0c;才能发挥出它真正的价值。虽然中国近几年已经飞速发展&#xff0c;但是我们的自主创新能力依然比不了发达国家。而且&#xff0c;尽管科研成果在理论和实践上已经取得了很大的进展&#xff0c;但如何将其落地到实际生产中仍然存在很多问题。其中…

如何训练Embedding Model

BGE的技术亮点&#xff1a; 高效预训练和大规模文本微调&#xff1b;在两个大规模语料集上采用了RetroMAE预训练算法&#xff0c;进一步增强了模型的语义表征能力&#xff1b;通过负采样和难负样例挖掘&#xff0c;增强了语义向量的判别力&#xff1b;借鉴Instruction Tuning的…

如何在k8s的Java服务镜像(Linux)中设置中文字体

问题描述&#xff1a;服务是基于springboot的Java服务&#xff0c;在项目上是通过Maven的谷歌插件打包&#xff0c;再由k8s部署的。k8s的镜像就是一个Java服务&#xff0c;Java服务用到了中文字体。 解决这个问题首先需要搞定镜像字体的问题。有很多类似的解决方案&#xff0c;…

window环境下安装node.js8+angular6

安装node.js8 1.登录node官网 node官网Node.js is a JavaScript runtime built on Chromes V8 JavaScript engine.https://nodejs.org/en2.找到适合自己的版本 选择下载 安装的时候一键next傻瓜式安装即可 设置淘宝镜像&#xff1a; npm config set registry https://regist…

动态增删kdtree(ikdtree)主要思路

ikdtree本质上也是一种kdtree,基本的构造方法和kdtree是一样的&#xff0c;本文主要记录两者不一样的地方&#xff0c;以港大MaRS实验室最新开源的增量式 kd-tree&#xff08;https://github.com/hku-mars/ikd-Tree&#xff09;里面的一些代码作为示范。 以下是ikdtree结构体包…

【Linux精讲系列】——yum软件包管理

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;Linux系统软件包管理工具yum讲解 &#x1f604;每日一言&#xff1a;踏向彼岸的每一步&#xff0c;都是到达彼岸本身。 目录 前言 Linux系统下的软件下载方式 yum 查看软件包 如何安装软件 如何卸…

业务连续性和恢复性计划:保障企业IT业务的可靠性

第一章&#xff1a;引言 在当今数字化时代&#xff0c;企业对信息技术的依赖愈发显著&#xff0c;IT系统的稳定性和可用性变得至关重要。无论是自然灾害、硬件故障还是网络攻击&#xff0c;都可能导致业务中断&#xff0c;从而带来严重的经济损失和声誉损害。为了保障企业IT业…