【03】C++ 类和对象 2:默认成员函数

文章目录

  • 🌈 前言
  • 🌈 Ⅰ 构造函数
    • 1. 构造函数概念
    • 2. 构造函数特性
    • 3. 初始化列表
  • 🌈 Ⅱ 析构函数
    • 1. 析构函数概念
    • 2. 析构函数特性
  • 🌈 Ⅲ 拷贝构造
    • 1. 拷贝构造概念
    • 2. 拷贝构造特性
    • 3. 深度拷贝构造
  • 🌈 Ⅳ 赋值重载
    • 1. 运算符重载
    • 2. 赋值运算符重载

🌈 前言

1. 默认成员函数介绍

  • 一个什么成员都没有的类简称为空类,编译器会自动为空类生成几个默认成员函数。
  • 默认成员函数:用户不写出来的话,编译器就会生成的成员函数称为默认成员函数。
  • 编译器自动生成的默认成员函数一般都比较挫,因此在大多数情况下就需要我们自己去编写这些个默认成员函数的执行逻辑。

2. 默认成员函数分类

函数功能
构造函数主要完成对成员变量的初始化工作
析构函数主要完成对成员变量的清理工作
拷贝构造使用同类对象初始化新创建的对象
赋值重载把一个对象赋值给另一个对象

🌈 Ⅰ 构造函数

1. 构造函数概念

  • 现定义一个日期 (date) 类
class date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{date d1, d2;d1.Init(2024, 2, 6);	// 调用公有成员函数 Init 为对象 d1 内的成员变量初始化d2.Init(2024, 2, 7);	// 调用公有成员函数 Init 为对象 d2 内的成员变量初始化return 0;
}
  • 对于 date 类,可以使用 Init 公有成员函数来给对象设置日期,但如果每次创建对象时都需要调用该函数来进行成员变量的初始化,需要写两行且很容易忘记,此时构造函数就诞生了。
  • 构造函数是一个特殊的成员函数,该成员函数的名字与类名一致,实例化对象时由编译器自动调用。用来保证每个对象内的成员变量都有一个适当的初始值,且在对象整个生命周期内只调用一次

2. 构造函数特性

  • 构造函数的主要任务是初始化对象

1. 构造函数特性

  1. 函数名和类型相同。
  2. 没有任何返回值。
  3. 实例化对象时编译器自动调用对应的构造函数。
  4. 构造函数也支持重载。
  5. 构造函数也支持缺省参数。

2. 构造函数示例

class date
{
public:// 构造函数:函数名和类名相同,没有返回值,支持缺省参数date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{date d1;				// 不指定初始值时就用缺省参数初始化对象date d2(2024, 2, 7);	// 在实例化对象的同时顺带就能初始化对象return 0;
}

在这里插入图片描述

  • d1 使用缺省参数完成了对象初始化,d2 使用给的值完成了对象初始化。都自动调用了构造函数。

3. 初始化列表

1. 为何使用初始化列表

  • 在构造函数的函数体内对成员变量初始化称为初始化赋值,并不是正儿八经的初始化,是赋值就存在多次赋值的问题。
  • 初始化赋值的问题在构造函数的函数体内没办法解决。因此在构造函数时可以使用一种叫做初始化列表的方式进行初始化。用以确保每个成员变量都只被初始化一次。
class date
{
public:date(int year, int month, int day){_year = year;_month = month;_day = day;_year = 2023;	// 成员变量 _year 被初始化了 2 次这咋个整}
private:int _year;int _month;int _day;
};

2. 初始化列表语法格式

类名(形参列表):成员变量1(成员变量 1 的初始值),成员变量2(成员变量 2 的初始值),成员变量n(成员变量 n 的初始值)
{}

3. 初始化列表示例

class date
{
public:date(int year = 1, int month = 1, int day = 1):_arr((int*)malloc(4 * sizeof(int))),_year(year),_month(month),_day(day){cout << "这是一个构造函数" << endl;}
private:int* _arr;int _year;int _month;int _day;
};

在这里插入图片描述

4. 初始化列表的特性

  1. 每个成员变量在初始化列表中只能出现一次 (只能初始化一次)。
  2. 以下成员变量必须放在初始化列表中进行初始化 (在函数体内对这些成员变量初始化会报错)。
    • 引用成员变量
    • const 成员变量
    • 自定义类型成员 (且该类没有默认构造函数时)
  3. 尽量使用初始化列表进行初始化,因为编译器会优先使用初始化列表。
  4. 成员变量的声明顺序就是成员变量在初始化列表中初始化的顺序。

在这里插入图片描述

🌈 Ⅱ 析构函数

1. 析构函数概念

1. 概念

  • 构造函数将对象内的成员变量初始化,那么析构函数就是将其销毁。
  • 析构函数不是完成对对象本身的销毁,对象是在出了对象所在的作用域或者程序结束时自动销毁。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2. 格式

~类名()
{	// 函数体
}

2. 析构函数特性

1. 析构函数特性

  1. 析构函数的函数名由 ~ 和 类名 两部分构成。
  2. 析构函数没有任何参数,也没有返回值。
  3. 一个类中只能由一个析构函数。
  4. 析构函数不能实现函数重载。
  5. 对象的声明周期结束时,编译器会自动调用析构函数。
  6. 如果类初始化时没有申请资源 (开辟空间),析构函数可以不写,反之一定要写。

2. 析构函数示例

  • 现有一个为了实现栈而定义的类。
class stack					// 定义一个用于实现栈的类
{
public:stack(int capacity = 4)	// 构造函数:_array((int*)malloc(4 * sizeof(int))), _top(-1),_capacity(capacity){cout << "stack(int capacity = 4)" << endl;}~stack()				// 析构函数:对象的生命周期结束时自动调用析构函数{free(_array);		// 如果有动态开辟的空间,就不用怕最后会忘记释放了_top = 0;_capacity = 0;cout << "~stack()" << endl;}
private:int* _array;int _top;int _capacity;
};
  • 使用上述 stack 类定义出对象的话就肯定要动态开辟空间,如果没有析构函数自动将开辟的空间释放掉,而自己又忘了将开辟的空间手动释放,内存泄漏这不就来了。

在这里插入图片描述

🌈 Ⅲ 拷贝构造

1. 拷贝构造概念

1. 拷贝构造概念

  • 在实例化对象时,可以不给初始值让构造函数使用缺省参数,也可以给初始值让构造函数对对象进行初始化。拷贝构造就是用一个现有的同类对象去初始化另一个对象

  • 拷贝构造函数只有一个形参 (只显示一个形参,this 指针不显示),该形参是对本类类型对象的引用 (一般常用 const 修饰),在用已存在的同类对象创建新对象时自动调用

2. 拷贝构造语法格式

类名(const 类名& 形参名)	// 实际上还是有两个形参,第一个形参为隐藏的 this 指针
{// 拷贝构造的函数体
}

3. 拷贝构造函数示例

class date
{
public:// 构造函数date(int year = 1, int month = 1, int day = 1): _day(day)	,_year(year),_month(month){}// 拷贝构造函数date(const date& d):_year(d._year),_month(d._month),_day(d._day){}
private:int _year;int _month;int _day;
};int main()
{date d1(2024, 2, 8);	// 调用构造函数对 d1 进行初始化date d2(d1);			// 调用拷贝构造使用 d1 对 d2 进行初始化return 0;
}

在这里插入图片描述

2. 拷贝构造特性

  1. 拷贝构造函数是构造函数的一个函数重载形式,本质还是构造函数。
  2. 拷贝构造函数的显示参数只有一个必须是对实参对象的引用
  3. 如果自己不写拷贝构造函数,编译器会自动生成,默认的拷贝构造函数执行的是浅拷贝。
  4. 拷贝构造函数最常用的调用场景:
    • 使用现有对象初始化创建新对象。
    • 函数参数类型为类类型对象。
    • 函数返回值为类类型对象。

3. 深度拷贝构造

1. 默认的拷贝构造函数执行的是浅拷贝

  • 浅拷贝:如果某个对象内的一个成员变量是一个指向一块连续空间的指针,那么浅拷贝就是将该地址拷贝给另一个对象的。两个对象各自的成员变量指向同一块空间。
  • 深拷贝:为新对象重新开辟一块同样大小的空间,并且将已有对象内的值拷贝过去。

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

2. 深度拷贝构造示例

class stack
{
public:stack(int capacity = 4)	// 构造函数{_array = (int*)malloc(sizeof(int) * capacity);assert(_array);_top = -1;_capacity = capacity;}stack(const stack& s)	// 拷贝构造,this 是 st2,s 是 st1{int* tmp = (int*)malloc(s._capacity * sizeof(int));assert(tmp);// 将 st1 的 array 中的有效数据拷贝给 st2 的 arraymemcpy(tmp, s._array, sizeof(int) * (s._top + 1));_array = tmp;_top = s._top;_capacity = s._capacity;}
private:int* _array;int _top;int _capacity;
};

在这里插入图片描述

🌈 Ⅳ 赋值重载

1. 运算符重载

1. 运算符重载概述

  • 有些时候函数名无法一眼看出该函数是为了实现什么功能 (如 func1、func2 这种函数名完全看不出该函数是要用来干什么)。
  • 运算符重载是具有特殊函数名的函数,是为了增强代码的可读性而被引入。

2. 运算符重载格式

函数返回值类型 operator操作符(形参列表)
{函数体
}

3. 赋值运算符示例

  • 现在要判断两个日期类的对象是否相等,内置操作符无法直接进行判断。因此将 == 进行重载以表示这是一个判断对象是否相等的成员函数。
class date
{
public:date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 将 == 重载成判断两个类类型对象是否相等的运算符bool operator== (const date& d)	// 此处的 this 表示 d1,d 表示 d2{return _year == d._year && _month == d._month && _day == d._day;}
private:int _year;int _month;int _day;
};

在这里插入图片描述

4. 运算符重载本质

  • 上述的 d1 == d2 实际上在编译器看来是 d1.operator==(d2),本质上还是调用对应的成员函数,然后将 d1 的地址传给 this 指针,形参 d 引用了 d2。

5. 运算符重载特性

  1. 不能通过连接其他符号来创建新的操作符,如 operator 和 @ 组成的 operator@ 不是一个新的操作符。
  2. 重载操作符必须有一个自定义类型的参数。
  3. 不能改变用于内置类型的运算符的含义,如 不能将 加法 的含义重载成 减法。
  4. 除了以下 5 种运算符,其余运算符都能被重载:
    • 点星 ( .* ),域作用限定符 ( :: ),计算大小 ( sizeof ),三目运算符 ( ?: ),点 ( . )

2. 赋值运算符重载

1. 赋值重载功能

  • 实现类类型对象之间的赋值,现在有两个已经被实例化好的对象 A、B,赋值重载就是将 对象 A 的值赋值给 对象 B。
  • 和拷贝构造不一样,拷贝构造是用一个定义好的对象去初始化一个未被定义的对象。

2. 赋值重载格式

  • 形参类型:const 类名&,传引用可以提高传参效率。
  • 返回值类型:类名&,设置返回值是为了支持连续赋值 (A = B = C 这样)。
  • 检测是否自己给自己赋值
  • 返回 *this,为了实现连续赋值。

3. 赋值重载示例

class date
{
public:date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}date& operator=(const date& d)	// this 指针指向 d1,d 表示 d2{if (this != &d)				// 避免自己给自己赋值{_year = d._year;_month = d._month;_day = d._day;}return *this;				// 返回对 d1 的引用}
private:int _year;int _month;int _day;
};

在这里插入图片描述

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

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

相关文章

ruoyi若依框架SpringSecurity实现分析

系列文章 ruoyi若依框架学习笔记-01 ruoyi若依框架分页实现分析 ruoyi若依框架SpringSecurity实现分析 文章目录 系列文章前言具体分析一、项目中的SpringSecurity版本二、登录认证流程分析三、权限鉴定四、退出登录五、SpringSecurity配置类 总结 前言 在ruoyi-vue若依框…

单片机的省电模式及策略

目录 一、单片机省电的核心策略 二、单片机IO口的几种模式 三、单片机的掉电运行模式 &#xff08;1&#xff09; 浅谈cpu运行为什么会需要时钟&#xff1f; &#xff08;2&#xff09;STC15系列单片机内部可以配置时钟 &#xff08;3&#xff09;分频策略&#xff0c;降低…

数据结构与算法之美学习笔记:51 | 并行算法:如何利用并行处理提高算法的执行效率?

目录 前言并行排序并行查找并行字符串匹配并行搜索总结引申 前言 本节课程思维导图&#xff1a; 时间复杂度是衡量算法执行效率的一种标准。但是&#xff0c;时间复杂度并不能跟性能划等号。在真实的软件开发中&#xff0c;即便在不降低时间复杂度的情况下&#xff0c;也可以…

Java面试题2024(Java面试八股文)

文章目录 基础Springspring Mybatis数据库Mysql redis并发编程网络通信消息队列MQ分布式分布式事务 设计模式 更新中 基础 Java基础 Java对象的创建 集合 HashMap详解 HashMap实现原理 ConcurrentHashMap原理详解 反射 JAVA反射详解 异常 Java 的异常体系 泛型 Java泛型详解 …

Linux应用开发---网络通信

Linux应用开发—网络通信 1 网络通信概述 Linux下的网络编程&#xff0c;我们一般称为 socket 编程&#xff0c;socket 是内核向应用层提供的一套网络编程接口&#xff0c;我们可以基于socket接口开发自己的网络相关应用程序。 1.1 socket 简介 套接字&#xff08;socket&…

Jenkins升级后,构建任务配置界面重复错位

最近我把公司的Jenkins服务升级到了最新版本&#xff0c;升级完成后&#xff0c;点了一下构建任务&#xff0c;发现能够构建成功&#xff0c;就以为顺利完成升级了&#xff0c;下班走了&#xff0c;结果第二天&#xff0c;进入构建任务配置界面发现&#xff0c;界面一团乱麻&am…

LabVIEW热电偶自动校准系统

设计并实现一套基于LabVIEW平台的工业热电偶自动校准系统&#xff0c;通过自动化技术提高校准效率和精度&#xff0c;降低人力成本&#xff0c;确保温度测量的准确性和可靠性。 工业生产过程中&#xff0c;温度的准确测量对产品质量控制至关重要。传统的热电偶校准方式依赖人工…

[Java][算法 双指针]Day 02---LeetCode 热题 100---04~07

LeetCode 热题 100---04~07 第一题&#xff1a;移动零 思路 找到每一个为0的元素 然后移到数组的最后 但是需要注意的是 要在给定的数组原地进行修改 并且其他非零元素的相对顺序不能改变 我们采用双指针法 定义两个指针i和j i和j一开始分别都在0索引位置 然后判断j所…

tab 切换类交互功能实现

tab切换类交互&#xff1a; 记录激活项&#xff08;整个对象/id/index)动态类型控制 下面以一个地址 tab 切换业务功能为例&#xff1a; <div class"text item" :class"{active : activeAddress.id item.id}" click"switchAddress(item)"…

v-if 和v-for的联合规则及示例

第073个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使用&#xff0c;computed&a…

pytest+allure批量执行测试用例

在 Pytest 中,可以使用装饰器 `@pytest.fixture` 来定义用例级别的前置和后置操作。下面是一个示例代码,演示了如何使用 Pytest 的前置和后置操作: ```python import pytest @pytest.fixture(scope="function") def setup_function(): print("Setup fu…

《游戏引擎架构》 -- 学习2

声明&#xff0c;定义&#xff0c;以及链接规范 翻译单元 声明与定义 链接规范 C/C 内存布局 可执行映像 程序堆栈 动态分配的堆 对象的内存布局 kilobyte 和 kibibyte 流水线缓存以及优化 未完待续。。。

conda创建环境,查看环境,激活环境,查看包,复制环境,删除环境

创建环境 conda create --name [yourEnvname]查看环境 conda env list激活环境 conda activate [yourEnvname]查看环境下的包 conda list复制环境&#xff1a;假设已有环境名为A&#xff0c;需要生成的环境名为B conda create -n B --clone A删除环境 conda remove -n [y…

如何利用腾讯工蜂提升广告推广和用户运营效率

无代码开发&#xff1a;腾讯工蜂的连接优势 在广告推广和用户运营中&#xff0c;腾讯工蜂的无代码开发优势让广告系统和用户运营系统能够轻松地实现无需API开发的集成。这使得没有专业编程技能的工作人员也能通过腾讯工蜂的用户友好界面&#xff0c;实现系统的快速连接和集成&…

svn常用命令及过滤文件 global ignore pattern

SVN常用命令详解和global ignore pattern Subversion&#xff08;SVN&#xff09;是一个版本控制系统&#xff0c;广泛用于软件开发项目中。它能够追踪文件的变更&#xff0c;并且允许多人在同一个项目中协同工作。以下是一些常用的SVN命令及其用法。 1. 检出代码 要从SVN服…

SQL注入(SQL Injection)从注入到拖库 —— 简单的手工注入实战指南精讲

基本SQL注入步骤&#xff1a; 识别目标&#xff1a;确定目标网站或应用程序存在潜在的SQL注入漏洞。收集信息&#xff1a;通过查看页面源代码、URL参数和可能的错误信息等&#xff0c;搜集与注入有关的信息。判断注入点&#xff1a;确定可以注入的位置&#xff0c;比如输入框、…

20240208问题解决

问题解决 armbian输入指令 sudo - i出问题 一些教程 Alist–集分享挂载功能于一身的网盘工具 | Laoyutang https://blog.laoyutang.cn/linux/alist.html docker的启动方式 Docker --restart的参数类型有&#xff1a; no 。默认值&#xff0c;表示容器退出时&#xff0c;Do…

Elasticsearch 安装和配置脚本文档

Elasticsearch 安装和配置脚本文档 目录 **Elasticsearch 安装和配置脚本文档**0.**概述**1.**使用方法&#xff1a;**2.**脚本步骤&#xff1a;**3. **完整代码如下&#xff1a;** 0.概述 此Bash脚本用于自动化在CentOS 7系统上安装和配置Elasticsearch&#xff08;ES&#x…

浅聊一下redis的雪崩,穿透和击穿

雪崩&#xff08;Cache Avalanche&#xff09;&#xff1a; 定义&#xff1a; 雪崩是指缓存中的大量数据在同一时间失效或过期&#xff0c;导致大量的请求直接访问底层数据库或服务&#xff0c;从而对数据库或服务造成巨大的压力。例子&#xff1a; 假设有一组缓存键&#xff0…

pytorch dataloader 中collate_fn是什么

collate_fn&#xff08;collate function&#xff09;是在 PyTorch 中 DataLoader 中使用的一个参数&#xff0c;用于自定义数据加载和批处理的方式。在训练神经网络时&#xff0c;通常会将数据划分成小批量进行处理&#xff0c;collate_fn 就是用来指定如何将单个样本组合成小…