【C++初阶】四、类和对象(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【C++初阶】三、类和对象
(面向过程、class类、类的访问限定符和封装、类的实例化、类对象模型、this指针)
-CSDN博客

 =========================================================================

                     

引入:类的六个默认成员函数

如果一个类中什么成员都没有简称为空类
但空类中并不是什么都没有任何类在什么都不写时
编译器自动生成以下六个默认成员函数
默认成员函数用户没有显式实现编译器自动生成成员函数称为默认成员函数

                     

  • 初始化和清理
    构造函数(1) -- 完成成员变量的初始化工作
    析构函数(2) -- 完成一个对象结束生命周期后的资源清理工作
                  
  • 拷贝复制
    拷贝构造函数(3) -- 使用同类对象初始化创建对象
    赋值重载(4) -- 把一个对象赋值给另一个对象
                    
  • 取地址重载
    主要是普通对象(5)const对象取地址(6),这两个很少会自己实现

                 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                     

一 . 构造函数(难)

构造函数的概念和特性:

                   

C++构造函数的概念:

还是假设有以下Date类:
//日期类:
class Date
{
public:
	//我们自己定义的初始化函数:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//打印日期函数:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	//私有成员函数:
	int _year; //年
	int _month; //月
	int _day; //日
};

int main()
{
	Date d1;
	d1.Init(2023, 11, 16);
	d1.Print();

	Date d1;
	d1.Init(2023, 11, 17);
	d1.Print();

	return 0;
}
  • 对于以上的Date类,可以通过我们自己定义的 Init共有函数方法给对象设置日期

    如果每次创建对象都需要调用该方法初始化对象成员的话

    有点麻烦而且可能会忘记初始化,那能否在对象创建时就自动进行初始化呢?

                        
  • C++为了优化C语言需要自己初始化的情况,有了一个新概念构造函数
    构造函数特殊的成员函数其名字类名相同
    创建类类型对象时由编译器自动调用进行对象的初始化
    保证每个数据成员都有一合适的初始值,并且在对象整个声明周期内只会调用一次
                      
  • 构造函数分为有参构造函数无参构造函数
    我们
    创建对象时可以设置各成员变量初始化的值
    如果
    没有设置,则对象初始化时会调用无参构造函数
    如果
    设置了,则对象初始化时会调用相应的有参构造函数

Date类 -- 图示:

                  

主函数通过构造函数创建对象 -- 图示:

                          

                          
---------------------------------------------------------------------------------------------

                    

C++构造函数特征:

                   

  • 构造函数名名相同构造函数没有返回值
    对象实例化编译器自动调用对应的构造函数
                    
  • 如果类中没有显式定义构造函数,则C++编译器自动生成一个无参的默认构造函数
    一旦用户显式定义构造函数编译器将不再自动生成构造函数
    所以如果定义了有参构造函数最好再定义一个无参构造函数
    防止创建对象时需要无参构造函数而又无法调用到
                    
  • 构造函数也是函数可以有参数,所以也可以对其设置缺省参数
    将一个有参构造函数初始化全部成员变量的构造函数
    所有参数都设置一个缺省参数全缺省构造函数),
    这样该构造函数就既实现了有参构造函数的任务
    又实现了无参构造函数的任务,因为初始化对象时如果不给初始化值
    那么有参构造函数的缺省参数会发挥作用实现无参构造函数的任务
    这样一个构造函数就可以替代有参无参两个构造函数
                
  • 构造函数支持重载虽然支持重载
    但如果已经定义了全缺省构造函数已经能够实现无参构造函数的情况下
    这时如果再定义一个无参构造函数虽然构成了构造函数重载
    但是实际调用时是会出错的,因为全缺省构造函数无参构造函数功能重复
    编译器就会不知道该调用哪个构造函数
                  
  • 无参的构造函数全缺省的构造函数都称为默认构造函数
    并且默认构造函数只能有一个否则会有调用歧义
    注意:
    无参构造函数全缺省构造函数编译器默认生成的构造函数
    都可以认为是默认构造函数
    不传参数还可以被调用的构造函数都可以叫默认构造函数
全缺省构造函数 -- 图示:

                            

编译器默认生成的构造函数的作用:
  • C++中把类型分成了内置类型基本类型自定义类型
    内置类型就是语言原生的数据类型intdouble指针……);
    自定义类型就是我们使用 class / struct / union 自己定义的类型
    关于编译器生成的默认构造函数该构造函数会对我们未定义的成员变量进行初始化
                   
  • 不同编译器初始化方式不同
    VS2013中:
    如果对象的成员变量为内置类型
    默认生成构造函数不会对其进行处理为随机值);
    如果对象的成员变量为自定义类型
    默认生成构造函数则会调用该自定义类型的默认构造函数
    VS2019情况会更复杂

    如果对象的成员变量全是内置类型
    默认生成构造函数不会对其进行处理为随机值);
    如果对象的成员变量既有内置类型又有自定义类型
    则会对其中的内置类型进行处理int类型成员变量会被初始化为0),
    对其中的自定义类型,会调用该自定义类型的默认构造函数
               
  • 所以默认生成的构造函数根据对象的成员变量的情况判断是否要对其进行处理
    如果对象的成员变量为自定义类型,就调用该自定义类型的默认构造函数
    如果是内置类型,则不进行处理为随机值
    会处理自定义类型不一定处理内置类型(看编译器),建议统一当成不会进行处理
图示:

           

  • 因此C++11中针对内置类型成员不初始化的缺陷,又打了一个补丁
    内置类型成员变量类中声明可以给默认值
    给了默认值又有定义显式构造函数的话显式构造函数为准
图示:

                     

总结:
  • 一般情况下我们都要自己写构造函数
                 
  • 成员变量如果都是自定义类型或者成员变量声明时给了缺省值
    那就可以考虑让编译器自己生成构造函数

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

二 . 析构函数

析构函数的概念和特性:

                 

C++析构函数的概念:

            

  • 通过前面对构造函数的了解,我们知道了一个对象是怎么来的
    可一个对象又是怎么没的呢?如果说构造函数是我们以前写的Init初始化函数

    那么析构函数就是我们以前写的Destroy销毁函数
                            

  • 析构函数构造函数功能相反析构函数不是完成对对象本身的销毁
    局部对象销毁工作是由编译器完成
    对象销毁时会自动调用析构函数完成对象中资源的清理工作

                          

                          
---------------------------------------------------------------------------------------------

                    

C++析构函数的特性:

                

  • 析构函数名 = 在类名前加上字符 ~” (按位取反符号
                        
  • 析构函数没有返回值函数参数
                
  • 一个类只能有一个析构函数没有显式定义编译器自动生成默认的析构函数
    析构函数不支持重载
                      
  • 对象声明周期结束C++编译系统自动调用析构函数

析构函数 -- 图示:

                            

编译器默认生成的构造函数的作用:
  • 默认生成的析构函数其行为跟构造函数的类似
    针对内置类型的成员变量析构函数不会对其进行处理
    针对自定义类型的成员变量析构函数也会调用该自定义类型的默认析构函数
                       
  • 如果类中没有申请资源析构函数可以不写直接使用编译器生成的默认析构函数
    比如之前写的Date日期类就可以不写
    而如果类中有申请资源则一定要写析构函数否则会导致资源内存泄漏
    比如Stack栈类需要显式定义析构函数进行资源清理
图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

三 . 拷贝构造函数(难)

拷贝构造函数的概念和特性:

                

C++拷贝构造函数的概念:

                     

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

图示:

                          

                          
---------------------------------------------------------------------------------------------

                    

C++拷贝构造函数的特性:

                       

  • 拷贝构造函数也是特殊的成员函数构造函数的一个重载形式
                     
  • 拷贝构造函数的参数只有一个必须是类类型对象的引用
    使用传值方式作为其参数编译器会直接崩溃因为会引发无穷递归调用
                           
  • 如果没有显式定义拷贝构造函数编译器会生成默认的拷贝构造函数
    默认的拷贝构造函数拷贝对象时会按内存存储按字节序完成拷贝
    这种拷贝叫做浅拷贝或者值拷贝
注:

在编译器生成的默认拷贝构造函数内置类型按照字节方式直接拷贝值拷贝),
自定义类型则会调用该自定义类型的拷贝构造函数完成拷贝

图示:

                

                

  • 编译器生成的默认拷贝构造函数已经可以完成字节序的值的拷贝值拷贝
    当类中没有涉及资源申请申请动态空间等
    浅拷贝已经足够使用是否显式定义拷贝构造函数都可以
    但是一旦涉及到了资源申请拷贝构造函数一定要显式定义进行深拷贝
​​​​​​​图示:

                   

  • 拷贝构造函数典型调用场景
    使用已存在的对象拷贝创建新对象函数参数类型类类型对象
    函数返回值类型类类型对象
注:

为了提高效率一般对象传参尽量使用引用类型返回
返回时根据实际场景能用引用返回尽量使用引用返回

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

四 . 赋值运算符重载

运算符重载的使用和注意事项

                   

引言:

对于内置类型intdouble……)数据我们可以直接对其使用运算符
假设我们有整型变量ab,我们可以对其使用
​​​​​​​a == b判断相等) 、a > b判断大小),
但对自定义类型的数据而言,就不能直接对其使用运算符
因为编译器不知道怎么判断我们自定义的类型,所以需要我们自己定义其判断的规则

图示 --  自定义类型判断规则:

                          

                          
---------------------------------------------------------------------------------------------

                    

运算符重载的使用:

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

图示 --  类外运算符重载:

                          

                          
---------------------------------------------------------------------------------------------

                    

运算符重载的注意事项:

  • 不能通过连接其它符号来创建新的操作符:比如operator@
                  
  • 重载操作符必须有一个类类型参数
                        
  • 用于内置类型的运算符含义不能改变
    例如内置的整型+ 不能改变其含义 ++= 是不一样的
                   
  • 重点
    作为类成员函数重载形参看起来比操作数数目少一个
    因为成员函数第一个参数为隐藏的this指针
                    
  • 注意以下五个运算符不能重载
    .* ”  、​“ :: ”  、“ sizeof ”  、“ ?: ”  、“ . ”​​​​​
图示 --  类中运算符重载:

                      

  • 一个类要重载哪些运算符主要看这个运算符对这个类来说有没有意义
    有意义可以重载没有意义不要重载
    日期类来说日期的 +() *() /() 都没有意义-() 有意义
    两个日期相减可以计算两日期相差了多少天
    日期+日期没有意义日期+整型有意义
    d1 + 100 计算d1日期的100天后的日期
图示 --  类中实现 += 和 + 运算符重载:

:“+=运算符重载中要设置返回值 -- return *this ,这里忘了写了
                     

                     


                    

赋值运算符(=)重载

                

赋值运算符 -- "=" ,赋值运算符重载就是让自定义类型也能像内置类型一样使用=

               

赋值运算符重载格式:

  • 参数类型const T&
    const修饰参数,能够防止赋值拷贝时左右值写反了,导致改变了原对象
    T& 传参引用接收右值的别名”,提高传参效率
                     ​​​​​​​
  • 返回值类型T&
    引用返回可以提高返回的效率设置返回值还为了支持=连续赋值
                     
  • 定义赋值运算符重载函数时需要检测是不是自己给自己赋值的情况
                         
  • 最终返回*this(即返回被赋值对象本身),能够符合=连续赋值的含义
                 
  • 用户没有显式实现编译器生成一个默认的赋值运算符重载函数
    行为拷贝构造函数类似
    针对内置类型成员变量:进行 值拷贝浅拷贝
    针对自定义类型成员变量:会调用该自定义类型的 赋值运算符重载函数
    注意:
    如果类中
    没有资源”(Date类),赋值运算符重载函数要不要显式定义都可以
    如果类中资源”(Stack类),赋值运算符重载函数必须要显式定义
                          
  • 赋值运算符只能重载成类的成员函数只能在类中定义重载),不能重载为全局函数
    原因

    赋值运算符重载函数如果不显式实现编译器生成一个默认的
    此时如果再在类外实现一个全局的赋值运算符重载函数
    就会和编译器在类中生成的默认赋值运算符重载函数冲突
    ​​​​​​​所以赋值运算符重载函数只能是类的成员函数

图示 -- 以Date类为例:

 ​​​​

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web(6)Metasploit缓冲区溢出漏洞

利用ms17_010漏洞建立会话&#xff1a; 原理&#xff1a; 因为window低级版本中存在漏洞&#xff0c;端口445.SMB是一个协议名&#xff0c;全称是Server Message Block&#xff08;服务器消息快协议&#xff09;&#xff0c;用于计算机之间共享文件&#xff0c;打印机&#x…

智驾芯片全矩阵「曝光」,这家企业的车载品牌正式官宣

随着汽车智能化加速&#xff0c;智能驾驶芯片格局逐渐清晰。 针对L0-L2&#xff0c;业内基本采用智能前视一体机方案&#xff1b;要实现高速NOA、城市NOA等更为高阶的智驾功能等&#xff0c;则基本采用域控制器方案。从前视一体机至域控&#xff0c;再逐步演进到舱驾一体、中央…

eclipse项目移到idea上部署运行

1.配置web模块 另外&#xff0c;模块这里&#xff0c;也要加上Spring 2.配置Artifact &#xff08;用于tomcat&#xff09; 就是从上面配置的web模块&#xff0c;产生的工件 3.添加lib 一般是在web-inf/lib &#xff0c; 遇到的坑&#xff1a; jdk版本问题&#xff0c;这里…

加速软件开发:自动化测试在持续集成中的重要作用!

持续集成的自动化测试 如今互联网软件的开发、测试和发布&#xff0c;已经形成了一套非常标准的流程&#xff0c;最重要的组成部分就是持续集成&#xff08;Continuous integration&#xff0c;简称CI&#xff0c;目前主要的持续集成系统是Jenkins&#xff09;。 那么什么是持…

6.显示评论 + 添加评论

1.显示评论 数据层&#xff1a;根据实体查询一页评论数据、根据实体查询评论的数量业务层&#xff1a;处理查询评论的业务、处理查询评论数量的业务表现层&#xff1a;显示帖子详情数据时&#xff0c;同时显示该帖子所有的评论数据 1.1 数据访问层 entity_type&#xff1a;实体…

linux 内存回收mglru算法代码注释2

mglru与原lru算法的兼容 旧的lru算法有active与inactive两代lru&#xff0c;可参考linux 内存回收代码注释&#xff08;未实现多代lru版本&#xff09;-CSDN博客 新的算法在引入4代lru的同时&#xff0c;还引入了tier的概念。 新旧算法的切换的实现在lru_gen_change_state&a…

机器学习探索计划——数据集划分

文章目录 导包手写数据划分函数使用sklearn内置的划分数据函数stratifyy理解举例 导包 import numpy as np from matplotlib import pyplot as plt from sklearn.datasets import make_blobs手写数据划分函数 x, y make_blobs(n_samples 300,n_features 2,centers 3,clus…

【UE5】资源(Asset)

了解UE游戏的基本构成 资源&#xff08;Asset&#xff09;: 在UE中&#xff0c;资源&#xff08;Asset&#xff09;是指游戏中使用到的各种素材&#xff0c;例如模型、纹理、材质、声音、动画、蓝图、数据表格、关卡等&#xff08;通常以uasset结尾&#xff09;&#xff0c;他…

freeswitch设置多个execute_on_media

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 fs中有非常多的接口和通道变量&#xff0c;使用方式多变。 官方文档有时候也仅仅是介绍了最基本的使用方法和格式。 环境 centos&#xff1a;CentOS release 7.0 (Final)或以上版本 freeswitch&#xff1a;v1.6 G…

办公技巧:Word中插入图片、形状、文本框排版技巧

目录 一、插入图片排版技巧 二、添加形状排版技巧 三、插入“文本框”排版技巧 我们平常在制作word时候经常会遇到插入选项卡下的图片、形状和文本框这三种情况下&#xff0c;那么如何使得Word文档当中添加这三个元素的同时&#xff0c;又能保证样式美观呢&#xff0c;今天小…

Leetcode200. 岛屿数量

Every day a Leetcode 题目来源&#xff1a;200. 岛屿数量 解法1&#xff1a;深度优先搜索 设目前指针指向一个岛屿中的某一点 (i, j)&#xff0c;寻找包括此点的岛屿边界。 从 (i, j) 向此点的上下左右 (i1,j)&#xff0c;(i-1,j)&#xff0c;(i,j1)&#xff0c;(i,j-1) …

静态链表的结构设计与主要操作功能的实现(初始化,头插,尾插,判空,删除,输出,清空,销毁)

目录 一.静态链表的结构设计 二.静态链表的结构设计示意图 三.静态链表的实现 四.静态链表的总结 一.静态链表的结构设计 typedef struct SNode {int data;//数据int next;//后继指针(下标) }SNode,SLinkList[MAXSIZE]; 二.静态链表的结构设计示意图 0:有效数据链的头节点;…

ATA-3080功率放大器在海底管道悬跨振动激振器检测中的应用

海底管道悬跨振动检测是指对海底管道在悬跨&#xff08;即管道跨越两个支撑点之间的区域&#xff09;段发生的振动进行监测和分析的过程。为了实现海底管道悬跨振动检测&#xff0c;通常使用以下几种方法&#xff1a; 1.加速度传感器&#xff1a;通过在管道表面安装加速度传感器…

现在可以手动获取真随机数吗?

获取真正的随机数并不像获取伪随机数那样简单&#xff0c;因为真随机数的产生依赖于物理过程或者其他难以预测的现象。在计算机科学中&#xff0c;通常使用的是伪随机数&#xff0c;它们是通过算法生成的&#xff0c;看起来像是随机的&#xff0c;但实际上是可以重现的。 如果…

新生儿散光:原因、科普和注意事项

引言&#xff1a; 散光是一种常见的眼睛问题&#xff0c;虽然在新生儿时期相对较少见&#xff0c;但了解其原因、科普相关知识&#xff0c;并提供一些建议的注意事项&#xff0c;对于婴儿的视力健康至关重要。本文将深入探讨新生儿散光的原因、相关科普知识&#xff0c;并为父…

新的centos7.9安装jenkins—(一)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 因为是用java8&#xff0c;所以还是要最后java8版本的jenkins&#xff0c;版本号是2.346.3&#xff0c;后…

​ 一文带你了解多文件混淆加密

目录 &#x1f512; 一文带你了解 JavaScript 多文件混淆加密 ipaguard加密前 ipaguard加密后 ​ &#x1f512; 一文带你了解 JavaScript 多文件混淆加密 JavaScript 代码多文件混淆加密可以有效保护源代码不被他人轻易盗取。虽然前端的 JS 无法做到纯粹的加密&#xff0c…

Echarts 大屏注册自定义地图解析文件流报错问题解决

效果图: 1、首先通过后台接口获取到SVG图片的文件流,postman能够正确解析出文件流,前端调用api时需要设置返回的响应格式为image/svg+xml格式,否则解析失败 拿到文件流后是这样的 <?xml version="1.0" encoding="utf-8"?> <!-- Generator: …

6.3.WebRTC中的SDP类的结构

在上节课中呢&#xff0c;我向你介绍了sdp协议&#xff0c; 那这节课呢&#xff0c;我们再来看看web rtc中。是如何存储sdp的&#xff1f;也就是sdp的类结构&#xff0c;那在此之前呢&#xff1f;我们先对sdp的内容啊&#xff0c;做一下分类。因为在上节课中呢&#xff0c;虽然…

软件设计不是CRUD(6):低耦合模块设计实战——组织机构模块(上)

组织机构功能是应用系统中常见的业务功能之一&#xff0c;但是不同性质、不同行业背景、不同使用场景的应用系统对组织机构功能的要求可能完全不一样。所以使用这样的功能对低耦合模块设计进行示例性的讲解是比较具有代表性的。在后续的几篇文章中&#xff0c;我们会首先进行示…