【C++】类和对象(二)——构造/析构/拷贝构造函数

💗个人主页💗
⭐个人专栏——C++学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

  • 导读
  • 1. 默认成员函数
  • 2. 构造函数
    • 2.1 引入
    • 2.2 特性
    • 2.3 默认构造函数
  • 3. 析构函数
    • 3.1 概念
    • 3.2 特性
    • 3.3 默认析构函数
  • 4. 拷贝构造函数
    • 4.1 概念
    • 4.2 特性
    • 4.3 默认拷贝构造函数

导读

我们上次讲了类和对象的一些定义相关的知识,今天我们进一步的来讲构造函数、析构函数和拷贝构造函数。

1. 默认成员函数

如果一个类中什么成员都没有,简称为空类。
但是!
空类中并不是什么都没有。
类有六个默认的成员函数,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
在这里插入图片描述

2. 构造函数

2.1 引入

为什么要有构造函数?
我们引入下述代码来看:

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(2024, 1, 30);d1.Print();Date d2;d2.Init(2024, 1, 31);d2.Print();return 0;
}

对于Date类,我们可以使用成员函数Init()给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦。

那有没有什么办法能在创建对象时,自动将我们要传递的内容放置进去呢?
那就需要我们的构造函数了。

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有
一个合适的初始值,并且在对象整个生命周期内只调用一次。

2.2 特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

构造函数的特点包括:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
class Date
{
public:Date() //无参构造函数{_year = 1;_month = 1;_day = 1;}Date(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.Print();Date d2(2024, 1, 31);//调用带参构造函数d2.Print();return 0;
}

在这里插入图片描述
不给参数时就会调用 无参构造函数,给参数则会调用 带参构造函数。
注意事项:

  • 如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明。
class Date
{
public:Date() //无参构造函数{_year = 1;_month = 1;_day = 1;}Date(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();  //这样不能调用无参初始化return 0;
}
  • 这里如果调用带参构造函数,我们需要传递三个参数

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

class Date
{
public:/*// 如果用户显式定义了构造函数,编译器将不再生成Date(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类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函Date d1;d1.Print();return 0;
}

在这里插入图片描述
为什么是随机值呢?我们接着引入下一特性。

2.3 默认构造函数

我们上述使用默认构造函数时,生成的默认值似乎并没有什么用处。

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。

class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

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

class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
int main()
{Date d;d.Print();return 0;
}

在这里插入图片描述
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

**注意:**无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

3. 析构函数

3.1 概念

析构函数是一种特殊的成员函数,用于在对象销毁时执行清理工作。它的名称与类名相同,前面加上一个波浪线(~)。在C++中,每个类都可以有一个析构函数,它会在对象的生命周期结束时被自动调用。

3.2 特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
class Date {
public:Date(int year = 1, int month = 0, int day = 0) {_year = year;_month = month;_day = day;}void Print() {printf("%d-%d-%d\n", _year, _month, _day);}~Date() {// Date 类没有资源需要清理,所以Date不实现析构函都是可以的cout << "~Date()  " << endl;  // 测试一下,让他吱一声}private:int _year;int _month;int _day;
};int main(void)
{Date d1;d1.Print();Date d2(2024, 1, 31);d2.Print();return 0;
}

在这里插入图片描述
我们在之前学习数据结构时,多用malloc()等函数来开辟空间,最后也要写个函数释放空间,但有时候我们会忘记释放,这时就需要我们的析构函数了,我们不用特意的去调用,系统会自己帮我们调用。

typedef int StackDataType;
class Stack {
public:/* 构造函数 - StackInit */Stack(int capacity = 4) {  // 这里只需要一个capacity就够了,默认给4(利用缺省参数)_array = (StackDataType*)malloc(sizeof(StackDataType) * capacity);if (_array == NULL) {cout << "Malloc Failed!" << endl;exit(-1);}_top = 0;_capacity = capacity;}/* 析构函数 - StackDestroy */~Stack() {  free(_array);_array = nullptr;_top = _capacity = 0;}private:int* _array;size_t _top;size_t _capacity;
};int main(void)
{Stack s1;Stack s2(2); return 0;
}

3.3 默认析构函数

  1. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
    和默认构造函数一样:
  • 对于 “内置类型” 的成员变量:不作处理
  • 对于 “自定义类型” 的成员变量:会调用它对应的析构函数。
class Time
{
public:~Time(){cout << "调用了time的析构函数" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year = 1;int _month = 1;int _day = 1;// 自定义类型Time _time;
};int main()
{Date d1;return 0;
}

在这里插入图片描述

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

4. 拷贝构造函数

4.1 概念

拷贝构造函数是一种特殊的构造函数,用于创建对象的拷贝。它接受与对象类型相同的另一个对象作为参数,并使用该参数的值来初始化新对象。拷贝构造函数通常用于实现深拷贝,即创建一个对象的独立副本,而不是仅复制指针或引用。
拷贝构造函数的语法如下:

类名(const 类名& 另一个对象)
{// 初始化新对象的成员变量
}

4.2 特性

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

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
class Date
{
public:Date(int year = 2024, int month = 1, int day = 31){_year = year;_month = month;_day = day;}void Print() {printf("%d-%d-%d\n", _year, _month, _day);}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);d1.Print();d2.Print();return 0;
}

在这里插入图片描述
为什么要用引用呢?
如果拷贝构造函数使用了对象作为参数而不是引用,那么在调用拷贝构造函数时又需要创建一个新的对象,这会引起一次不必要的拷贝构造操作。而如果使用引用作为参数,就可以直接引用原对象,避免了对象的拷贝。

此外,如果拷贝构造函数使用了对象作为参数,在进行传递时会调用拷贝构造函数,而拷贝构造函数又需要调用拷贝构造函数,这会导致无限递归的问题。而使用引用作为参数,可以避免这种无限递归的情况。

4.3 默认拷贝构造函数

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

class Date {
public:Date(int year = 0, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}// Date(Date& d) {//     _year = d._year;//     _month = d._month;//     _day = d._day;// }void Print() {printf("%d-%d-%d\n", _year, _month, _day);}private:int _year;int _month;int _day;
};int main(void)
{Date d1(2024, 1, 31);Date d2(d1);d1.Print();d2.Print();return 0;
}

在这里插入图片描述
默认拷贝构造函数似乎和前面的默认构造函数以及默认析构函数不太一样,它能够解决我们的需求。
但是!
这并不意味着我们不用写拷贝构造函数,
类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

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

  1. 使用已存在对象创建新对象
  2. 函数参数类型为类类型对象
  3. 函数返回值类型为类类型对象
class Date
{
public:Date(int year, int minute, int day){cout << "Date(int,int,int):" << this << endl;}Date(const Date& d){cout << "Date(const Date& d):" << this << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};
Date Test(Date d)
{Date temp(d);return temp;
}
int main()
{Date d1(2024, 1, 31);Test(d1);return 0;
}

在这里插入图片描述

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

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

相关文章

2024 高级前端面试题之 Vue 「精选篇」

该内容主要整理关于 Vue 模块的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 Vue模块精选篇 1. 谈谈你对MVVM的理解2. 谈谈你对SPA单页面的理解3. Vue2.x 响应式数据原理4. Vue3.x 响应式数据原理5. Vue中如何检测数组变化6. Vue中如何进…

大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置

上一篇&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 目录 1. &#x1f959;Idea中配置Live Templates来快速生成代码片段 2. &#x1f959;Idea中配置文件模板自定义初始代码 3.&#x1f959;设置spark-submit提交程…

[NAND Flash 7.1] 闪存系统性能优化方向集锦?AC timing? Cache? 多路并发?

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< ​ 传送门 >>> 总目录 主页: 元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 优…

【排序算法】C语言实现随机快排,巨详细讲解

文章目录 &#x1f680;前言&#x1f680;快排的核心过程partition&#xff08;划分过程&#xff09;&#x1f680;快排1.0&#x1f680;随机快速排序&#x1f680;稳定性 &#x1f680;前言 铁子们好啊&#xff01;继续我们排序算法今天要讲的是快排&#xff0c;通常大家所说…

HBase表结构

HBase是非关系型数据库&#xff0c;是高可靠性、高性能、面向列、可伸缩、实时读写的分布式数据库。 HBase使用场景 大规模数据存储&#xff1a;如日志记录、数据库备份等。实时数据访问&#xff1a;如实时搜索、实时分析等。高性能读写&#xff1a;如高并发、低延迟的读写操…

【Java之HTML】

HTML 概念 互联网的产生&#xff1a;w3c的成立&#xff0c; ​ 互联网最开始设计的目的&#xff1a;看论文 ---->浏览器&#xff0c;HTML ​ 网络三要素&#xff1a;HTML HTTP URL HTML描述论文的格式 HTTP标记这个论文在网络上怎么传输 URL:指示这个论文在互联网的哪…

【SHUD】Windows下安装rSHUD

目录 说明一、安装R二、安装rSHUD自动安装依赖手动安装依赖安装rShud说明 rSHUD官网 rSHUD功能: 空间数据分析和操作。处理矢量和栅格数据,建立非结构三角网络。读/写SHUD模型的输入文件读取SHUD模型输出文件自动化模型调参水文数据时间序列分析二维三维数据可视化GIS空间数…

oj赛(双周赛第二十次)

目录 碰碰车 小码哥与机器人 小码哥的跳棋游戏 竖直打印机 排队 邮箱地址 碰碰车 难度:钻石 占用内存:128 M时间限制:1秒 游乐园玩碰碰车&#xff0c;其中有一种碰碰车是在一条直线上行驶。该碰碰车有一个初始朝向和初始位置&#xff0c;并且以每秒一个单位的速度向初始方…

多线程代码案例之单例模式

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 多线程代码案例之单例模式 单例…

【数据结构 04】单链表

一、链表简介 链表是一种物理存储结构上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表在结构上的分类&#xff1a; 1. 带头结点或无头结点 2. 单向或双向 3. 循环或非循环 虽然链表有多种结构类型&#xff0c;但是我么在实际开发中…

C++ 结构体的构造函数

详解&#xff1a; ListNode(int x, ListNode* next) : val(x), next(next) {} 是 ListNode 结构体的构造函数。 ListNode(int x, ListNode* next)&#xff1a;这是构造函数的声明部分。指定了构造函数的名称为 ListNode&#xff0c;参数列表包括一个整数 x 和一个指向 ListNode…

LLVM实战之交叉编译

所谓交叉编译指的是能够在一个平台(例如x86)编译并构建二进制文件,而在另一个平台(例如ARM)运行。编译二进制文件的机器称为主机(host),而运行生成的二进制文件的平台称为目标平台(target)。为相同平台(主机与目标机器相同)编译代码称为本机编译(native assembler…

【RT-DETR有效改进】Bi-FPN高效的双向特征金字塔网络(附yaml文件+完整代码)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本文给大家带来的改进机制是BiFPN双向特征金字塔网络,其是一种特征融合层的结构,也就是我们本文改进RT-DETR模型中的Neck部分,它的主要思想是通过多层级的特征金字塔和双向信息传递来提高精度。本文给大家带…

【C++】三角形(triangle)

题目描述 小魏有 3*n 根颜色两两不同的木棍&#xff0c;第i根的长度为a[i]。 小魏想把这3*n根木棍分成n组&#xff0c;每组三根&#xff0c;并且同一组的三根木棍可以组成一个三角形。 小魏想知道他有多少种不同的分组方案。认为两个分组方案是不同的,当且仅当两种方案组出来的…

Linux命令之 printf 详解

将字符串格式化输出 选项说明%s字符串%f浮点型%b相对应参数中包含转义字符时&#xff0c;可以使用此替换符进行替换&#xff0c;对应的转义字符被转义。%cASCII字符&#xff0c;显示想对应参数的第一个字符。%d%i 十进制整数%o不带正负号的八进制值%u不带正负号的十进制值%x不…

甘肃推动“安全石窟”建设,新技术助力文物预防性保护

一、甘肃用“智能化”技术让文物“重获新生” 文物保护与历史遗产和文化瑰宝的安全相关。甘肃有莫高窟、麦积山、炳灵寺等石窟寺&#xff0c;背负着历史的沧桑。但是&#xff0c;岁月侵蚀使这些文物状态变得令人堪忧。环境的微妙变化和温度和湿度的波动会对其导致不可逆转的伤…

Visual Studio 和Clion配置Cocos2d-x环境

Visual Studio 和Clion配置Cocos2d-x环境 首先&#xff0c;我就不贴图片的&#xff0c;懒得上传图床。懒。 开发环境: ​ 系统: Window11 ​ 编译器: CMake MSVC ​ 开发工具&#xff1a;Clion or Visual Studio ​ 请自行配置好&#xff0c;Python2.7&#xff0c;和Cma…

浮点数加、减运算步骤

一、浮点数介绍 1.1 浮点数格式&#xff1a; 精度位数格式单精度 float4个字节32位符号位1位&#xff0c;阶码8位&#xff0c;尾数23位双精度 double8个字节64位符号位1位&#xff0c;阶码11位&#xff0c;尾数52位 1.2 浮点的表示方法 浮点数在机器中的形式如下所示&#x…

C++数据结构与算法——链表

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

Linux初始相关配置

前言 在学完了Linux的相关基础命令后&#xff0c;在正式使用Linux系统之前&#xff0c;我觉得配置一些东西是很有意义的。 文章目录 前言1.权限配置&#xff0c;普通用户无法sudo提权2.vim配置3.vim其他操作4.动静态库5.gcc/g6.程序翻译的过程7.make/makefile8.cmake/CMakeLis…