【浅尝C++】C++类的6大默认成员函数——构造、析构及拷贝构造函数

在这里插入图片描述

🎈归属专栏:浅尝C++
🚗个人主页:Jammingpro
🐟记录一句:好想摆烂,又好想学习~~


文章前言:本篇文章简要介绍C++类的构造函数、析构函数及拷贝构造函数,介绍每个小点时,都会附上对应的代码,如果可能的话。余下3大默认成员函数将于后续文章中叙述,要不,整个文章太长了。


文章目录

  • 构造函数
    • 基本概念
    • 特性
  • 析构函数
    • 基本概念
    • 特性
  • 拷贝构造函数
    • 概念
    • 特征


我们知道,如果一个类什么都没有,那么它就是一个空类。空类真的什么都没有吗?答案是否定的。任何类在什么都不写时,编译器会自动生成以下6个默认成员函数,本文将先介绍构造函数、析构函数及拷贝构造函数。👇
ps:默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
在这里插入图片描述

构造函数

基本概念

在介绍构造函数之前,我们先回顾一下类的定义和调用👇。

#include<iostream>
using namespace std;class Date
{
public:void Init(int year, int month, int day){m_year = year;m_month = month;m_day = day;}void Print(){cout << m_year << " - " << m_month << " - " << m_day << endl;}
private:int m_year;int m_month;int m_day;
};int main()
{Date date;date.Init(2023, 1, 1);date.Print();return 0;
}

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象
特征如下:
①函数名与类名相同。
②无返回值。
③对象实例化时编译器自动调用对应的构造函数。
④构造函数可以重载。

#include<iostream>
using namespace std;class Date
{
public://以下两个构造函数,参数个数不同,构成重载Date(int year, int month, int day){//定义对象时,会自动打印下面这句内容cout << "Date(int year, int month, int day)被调用" << endl;m_year = year;m_month = month;m_day = day;}//构造函数名称与类名相同,且无返回值Date(int year){cout << "Date(int year)被调用" << endl;}
private:int m_year;int m_month;int m_day;
};int main()
{Date d1(2022, 12, 12);Date d3(2023);return 0;
}

⑤如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

#include<iostream>
using namespace std;class Date
{
public:/*提供该构造函数后,编译不再生成默认构造函数Date(int year, int month, int day){m_year = year;m_month = month;m_day = day;}*/
private:int m_year;int m_month;int m_day;
};int main()
{Date d1;//若将上面类中定义的构造函数注释掉,则此处不会出错,因为编译器自动生成了默认构造函数(不带参数的函数)//若使用上面类中定义的构造函数,则此处定义d1对象会报错,因为编译器不再自动生成默认构造函数return 0;
}

⑥关于编译器生成的默认成员函数,不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d1对象m_year/m_month/m_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员m_t调用的它的默认成员函数

#include<iostream>
using namespace std;class Time
{
public:Time(){cout << "Time构造函数" << endl;m_hour = m_minute = m_second = 0;}void Print(){cout << m_hour << ":" << m_minute << ":" << m_second << endl;}
private;int m_hour;int m_minute;int m_second;
};class Date
{
public:void Print(){cout << m_year << "-" << m_month << "-" << m_day << endl;m_t.Print();}
private:int m_year;int m_month;int m_day;
};int main()
{Date d;//d未调用Time的构造函数,但打印时,Time的各个成员变量均已初始化为1。说明编译器自动实现的Date的构造函数,会自动调用Time的构造函数。d.Print();return 0;
}

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

#include<iostream>
#include<string>
using namespace std;class Jammingpro
{
public:void Print(){cout << "我是 " << m_name << ",今年 " << m_age << " 岁" << endl;}
private:string m_name = "xioaming";int m_age = "18";
};int main()
{Jammingpro jam;jam.Print();//打印的是默认值return 0;
}

⑦无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。下面代码演示的是无参、全缺省构造函数,两个函数只能留下其中一个,不能同时存在。

class Date
{
public:Date(){}Date(int year = 2022, int month = 12, int day = 12){m_year = year;m_month = month;m_day = day;}
private:int m _year;int m_month;int m_day;
}

析构函数

基本概念

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

特性

析构函数是特殊的成员函数,其特征如下:
①析构函数名是在类名前加上字符 ~。
②无参数无返回值类型。
③一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
④对象生命周期结束时,C++编译系统系统自动调用析构函数。

#include<iostream>
using namespace std;class Stack
{
public://对象生命周期结束时,需要调用下面的析构函数~Stack(){cout << "~Stack()" << endl;}
private:char* m_mem;int m_size;int m_capacity;
};int main()
{Stack stk;return 0;
}

⑤关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

#include<iostream>
using namespace std;class Time
{
public://下面这句话会被打印,因为Date的自动生成的析构函数会调用自定义数据类型的析构函数~Time(){cout << "Time析构函数" << endl;}
private;int m_hour;int m_minute;int m_second;
};class Date
{
private:int m_year;int m_month;int m_day;
};int main()
{Date d;return 0;
}

⑥如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

class Stack 
{
public:Stack(){m_mem = new char[100];//在堆区申请了内存空间m_size = 0;m_capcpity;}~Stack(){delete[] m_mem;//必须使用析构函数释放资源}
private:char* m_mem;int m_size;int m_capacity;
}

拷贝构造函数

概念

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

特征

其特征如下:
①拷贝构造函数是构造函数的一个重载形式。
②拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

class Date
{
public:Date(int year, int month, int day){m_year = year;m_month = month;m_day = day;}//Date(const Date d) ->错误写法,将引发无穷递归Date(const Date& d)//->正确写法{m_year = d.m_year;m_month = d.m_month;m_day = d.m_day;}
private:int m_year;int m_month;int m_day;
}

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

class Stack
{
public:Stack(){m_mem = new char[100];m_size = 0;m_capacity = 100;}/*下面这个是编译器自动生成的Stack(const Stack& s){m_mem = s.m_mem;m_size = s.m_size;m_capacity = s.m_capacity;}*/Stack(const Stack& s){m_mem = new char[s.m_capacity];strcpy(m_mem, s.m_mem);m_size = s.m_size;m_capacity = s.m_capacity;}
private:char* m_mem;int m_size;int m_capacity;
}

④编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像Date类这样的类是没必要的。而像上面的Stack类是需要自己显示实现拷贝构造函数的。
编译器自动生成的拷贝构造函数是浅拷贝,也就是将属性值复制一份。对于下面图片中的s1s2,由于s2复制了s1m_mem的地址,导致两个栈指向同一内存空间,两个栈生命周期结束后均会调用析构函数,此时将执行两次delete[] m_mem,将导致内存重复释放而报错。
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
在这里插入图片描述
⑤拷贝构造函数典型调用场景:
使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象

#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){cout << "Date create" << endl;m_year = year;m_month = month;m_day = day;}Date(const Date& d){cout << "Date copy" << endl;m_year = d.m_year;m_month = d.m_month;m_day = d.m_day;}~Date(){cout << "~Date()" << endl;}
private:int m_year;int m_month;int m_day;
};Date Test(Date d)
{return d;
}int main()
{Date date(2023, 11, 11);Test(date);Date cp(date);return 0;
}

这段代码的执行结果如下,即调用1次构造函数,3次拷贝构造函数,4次析构函数:

Date create
Date copy
Date copy
~Date()
~Date()
Date copy
~Date()
~Date()

对于上面的代码,执行Date date(2023, 11, 11);时调用1次构造函数;执行Test(date);时传递参数,调用了1次拷贝构造函数,返回时,返回值又调用了1次拷贝构造函数,由于返回值产生的临时变量和Test的参数d离开Test函数后生命周期就结束了,因此分别调用了析构函数,即2次析构函数;执行Date cp(date)构造cp对象时调用了1次拷贝构造函数,main函数执行结束,date与cp生命周期结束,分别调用析构函数,即2次析构函数。
为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用


文章结语:这篇文章对C++中的构造函数、析构函数及拷贝构造函数进行了简要的介绍。后续文章将介绍余下3大默认成员函数。
🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

【Linux专题】http(s)代理

【赠送】IT技术视频教程&#xff0c;白拿不谢&#xff01;思科、华为、红帽、数据库、云计算等等_厦门微思网络的博客-CSDN博客文章浏览阅读444次。风和日丽&#xff0c;小微给你送福利~如果你是小微的老粉&#xff0c;这里有一份粉丝福利待领取...如果你是新粉关注到了小微&am…

【objectarx.net】table问题2:添加table后,保存时出错

添加table后&#xff0c;保存时出现以上对话框。 原因&#xff1a; tb.TableStyle db.Tablestyle; 对于这句代码&#xff0c;tb所在的数据库和db不是同一个。

java反射和注解3-仿照retrofit组装接口参数

本片文章将用反射和注解仿照retrofit只需要传入一个带有给定注解的接口&#xff0c;通过调用接口就能直接将传入的数据和注解进行结合&#xff0c;生成对应参数 1&#xff0c;自定义注解 对字段的修饰 Retention(RetentionPolicy.RUNTIME) Target(ElementType.PARAMETER) pu…

Win7 SP1 x64 安装 Python 出错解决方法

1 双击安装 python-3.7.9.exe &#xff0c;提示出错&#xff0c;log.file 显示需要 KB2533623&#xff0c;但在Microsoft Update Catalog 没有搜到&#xff0c;实验 KB4474419 也可以。 2 Microsoft Update Catalog 搜索 KB4474419 &#xff0c;选择 x64 位&#xff0c;下载&am…

Cache学习(4):Cache分配策略Cache更新策略Cache逐出策略

Cache的数据流 常用名词 Allocation 分配Eviction 驱逐分配策略和更新策略分别为当产生Cache miss和Cache hit的时候数据流的具体行为 1 Cache分配策略&#xff08;Cache Allocation Policy&#xff09; Cache的分配策略是指不同情况下为数据分配Cache Line的不同行为。Cac…

基本数据结构二叉树(3)

目录 4.二叉树链式结构的操作 4.1 前置说明 4.2二叉树的遍历 4.2.1 前序、中序以及后序遍历 4.3 节点个数以及高度等 4.二叉树链式结构的操作 4.1 前置说明 由于博主对二叉树的结果掌握还不够深入&#xff0c;因此在讲解相关操作前将手动创建一颗简单的二叉树&#xff0c…

金字塔原理 读书笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言第1篇 表达的逻辑第1章 为什么要用金字塔结构归类分组&#xff0c;将思想组织成金字塔自上而下表达&#xff0c;结论先行自下而上思考&#xff0c;总结概括 第2…

Unity 接入TapADN播放广告时闪退 LZ4JavaSafeCompressor

通过跟踪安卓日志&#xff0c;发现报如下错误 Didnt find class "com.tapadn.lz4.LZ4JavaSafeCompressor" 解决方案&#xff1a; 去掉Minify这边的勾选&#xff0c;再打包即可。

trino push down fliter

依据trino 432 版本而写 1. 调用链 2. 先是元数据&#xff0c;然后做扫描数据层的filter pushFilterIntoTableScan

数据导入与预处理-第7章-数据清理工具OpenRefine

文章目录 数据清理工具OpenRefineOpenRefine简介下载与安装配置创建项目操作列收起列移动列和重排列移除该列与移除列重新定义列标题撤销与重做导出数据 进阶操作数据排序数据归类重复检测数据填充文本过滤数据转换 总结 数据清理工具OpenRefine OpenRefine简介 OpenRefine是…

深度学习之基于百度飞桨PaddleOCR图像字符检测识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介主要特点使用步骤 二、功能三、系统四. 总结 一项目简介 # Introduction to PaddleOCR Image Character Detection and Recognition System Based on Baidu…

静态住宅IP代理实际应用:它的强大用途你知道吗?

静态住宅IP代理与动态IP代理相比&#xff0c;提供了更稳定的网络身份&#xff0c;使得企业在进行数据采集、区域定位营销和市场研究时更为高效。同时&#xff0c;它也是提高在线隐私保护和避免封禁的有效工具。 通过详细分析&#xff0c;你将能全面了解静态住宅IP代理的应用&a…

SAP_ABAP_编程基础_字符转换_内存表、jsonString 相互转换

SAP ABAP 顾问&#xff08;开发工程师&#xff09;能力模型_Terry谈企业数字化的博客-CSDN博客文章浏览阅读441次。目标&#xff1a;基于对SAP abap 顾问能力模型的梳理&#xff0c;给一年左右经验的abaper 快速成长为三年经验提供超级燃料&#xff01;https://blog.csdn.net/j…

【力扣:1707 1803】0-1字典树

思路&#xff1a;树上每个节点存储拥有该节点的数组元素的最小值&#xff0c;left节点表示0&#xff0c;right节点表示1&#xff0c;构建完成后遍历树当子节点没有比mi小的元素时直接输出-1&#xff0c;否则向下构造。 struct tree{int m;tree*leftnullptr,*rightnullptr;tree…

【传智杯】儒略历、评委打分、萝卜数据库题解

&#x1f34e; 博客主页&#xff1a;&#x1f319;披星戴月的贾维斯 &#x1f34e; 欢迎关注&#xff1a;&#x1f44d;点赞&#x1f343;收藏&#x1f525;留言 &#x1f347;系列专栏&#xff1a;&#x1f319; 蓝桥杯 &#x1f319;请不要相信胜利就像山坡上的蒲公英一样唾手…

pandas教程:2012 Federal Election Commission Database 2012联邦选举委员会数据库

文章目录 14.5 2012 Federal Election Commission Database&#xff08;2012联邦选举委员会数据库&#xff09;1 Donation Statistics by Occupation and Employer&#xff08;按职业与雇主划分的捐赠数据&#xff09;2 Bucketing Donation Amounts&#xff08;桶捐赠额&#x…

vue项目实战-电商后台管理系统

项目简介&#xff1a; 该项目为电商后台的管理系统。设计了登录页面。 管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面&#xff1a; 管理页面由五个子模块组成&#xff1a;用户管理&#xff0c;权限管理&#xff0c;商品管理&#xff0c;订单管理…

实验八-数据处理

目录 1.数据来源 2.对于All表&#xff1a; 3.对于sf 和sfweibo 表&#xff1a; 4. 对于base_info表&#xff1a; 5.导出最后结果到一个Excel文件中&#xff0c;完成数据处理。 1.数据来源 本次所分析的数据是通过爬虫抓取的微博数据。选取新浪微博为数据平台&#xff0c;选…

STM32CubeIDE(ADC)

学习链接&#xff1a;【HAL库详解】STM32 ADC HAL库使用_哔哩哔哩_bilibili 目录 一、概念 1、ADC介绍 2、ADC主要特征 二、ADC模式 1、查询模式 1.1 单通道采集软件配置 1.2 查询模式步奏 1.3 单通道采集主要代码 2、多通道采集 2.1 软件配置 2.2 主要代码 一、概…

【Linux】Linux第一个小程序 --- 进度条

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和Linux还有算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 …