C/C++语言基础--C++构造函数、析构函数、深拷贝与浅拷贝等等相关知识讲解
本专栏目的
- 更新C/C++的基础语法,包括C++的一些新特性
前言
- 周末休息了,没有更新,请大家见谅哈;
- 构造函数、析构函数可以说便随着C++每一个程序,故学构造函数、析构函数是必要的;
- C语言后面也会继续更新知识点,如内联汇编;
- 本人现在正在写一个C语言的图书管理系统,1000多行代码,包含之前所学的所有知识点,包括链表和顺序表等数据结构,请大家耐心等待!!预计国庆前写完更新。
文章目录
- 构造函数
- 析构函数
- 构造/析构函数调用机制
- 析构函数调用时间
- 构造/析构函数用途展示
- 构造函数分类
- 无参构造函数
- 有参构造函数
- 拷贝构造函数(赋值构造)
- 移动构造函数
- 深拷贝和浅拷贝
- 构造函数的初始化参数列表
- 初始化参数列表
- 类中类如何构造
构造函数
首先我们写一个学生类,定义一个公有函数,用来打印学生信息:
#include <iostream>class Student
{
public:void print() {std::cout << "学号: " << m_uid << " 姓名: " << m_name << " 年龄: " << m_age << std::endl;}
private:std:string m_uid;std::string m_name;int m_age;
}int main() {Student stu;stu.print();
}
这个时候,你肯定有一个疑问,创建出的这个学习信息,没有赋值!!!,那怎么赋值呢?这个时候你可能会想到在类中再定义一个API函数进行赋值,如下:
void setMessage(std::string uid, std::string name, int age) {m_uid = uid;m_name = name;m_age = age;
}
这样确实能够解决问题,那如果每次都要这样,不觉得太麻烦了么?,每次创建类都要额外在调用一个函数!!!
因此,C++大叔也考虑到了这一点,及发明出了构造函数这个“简单”的东西,它允许我们再创建对象的时候自动调用该函数,如我们将上面的学生类进行修改:
#include <iostream>class Student
{
public:// 方法一Student(std::string uid, std::string name, int age){m_uid = uid;m_name = name;m_age = age;}// 方法二,推荐:参数列表方法Student(std::string uid, std::string name, int age):m_uid(uid),m_name(name),m_age(age){}void print() {std::cout << "学号: " << m_uid << " 姓名: " << m_name << " 年龄: " << m_age << std::endl;}
private:std:string m_uid;std::string m_name;int m_age;
}int main() {Student stu("123456", "wy", 18); // 创建时候赋值stu.print();
}
这样写无论从逻辑上,还是再写代码简约上,都好很多。
构造函数特点:
- 构造函数名和类名相同
- 构造函数可以重载
- 构造函数没有返回类型声明
调用:
- 自动调用(隐式),一般默认情况下C++编译器会自动调用构造函数(无参构造)
- 手动调用(显示),在一些情况下则需要手工调用构造函数(有参构造)
析构函数
当对象释放时,我们可能需释放/清理对象里面的某些资源,如果再类中对某一个变量,如:成员变量申请了一块内存,而在应对稍微复杂一点的项目,就很容易忘记释放内存,为了解决这个问题,C++提供了析构函数来处理对象的清理工作。析构函数和构造函数类似,不需要用户来调用它,而是在释放对象时自动执行。
特点:
- 析构函数名和类名相同,但是得在前面加一个波浪号**~**
- 析构函数只能有一个
- 构造函数没有返回类型声明
构造/析构函数调用机制
当定义了多个对象时,构造与析构的顺序是怎么样的呢?
#include <iostream>using namespace std;class Test
{
public:Test(int id):m_id(id){cout << m_id << " " << __FUNCTION__ << endl;}~Test(){cout << m_id << " " << __FUNCTION__ << endl;}
private:int m_id;
};void test()
{Test t1(1);Test t2(2);
}int main()
{test();return 0;
}
结果:
结论:
- 先创建的对象先构造,后创建的对象后构造
- 先创建的对象后析构,后创建的对象先析构
这个原因和函数调用内存有关,函数调用是压栈和出栈的过程,如果就想弄清楚,请看计算机系统相关的书籍,如:csapp
析构函数调用时间
- 在该对象生命周期结束后调用
构造/析构函数用途展示
构造函数:可以用来初始化对象,而且不需要显式调用,方便,快捷
析构函数:可以用来释放对象, 一次写好,没有后顾之忧(如:经常忘记delete、free)
class Man
{
public:Man(){age = 18;name = new char[20]{0};strcpy(name,"maye");}~Man(){if(name!=nullptr){delete[] name;name = nullptr;}}void print(){cout<<age<<" "<<name<<endl;}
private:int age;char* name;
}
这样就可以避免自己忘记释放内存的情况了。
构造函数分类
构造函数是可以重载的,根据参数类型和作用可以分为以下几类:
无参构造函数
- 直接创建对象即可自动调用
Test te;
注意:不要在对象后面加(),无参构造函数不能显式调用
有参构造函数
-
有三种调用方法
//1,括号法 Test t1(20,"cc"); t1.print(); //2,赋值符号 Test t2 = {18,"wy"}; t2.print(); //3,匿名对象 Test t3 = Test(90,"wy"); t3.print(); //注意: Test tt; //error:类Test不存在默认构造函数,因为自己定义了构造函数//** 匿名对象如果没有值来接收,那么就会被立即释放 ** Int(2, 3); //会立即释放Int f = Int(2,3); //就不会立即释放
如果没有写有参构造函数,那么C++编译器会自动帮我们生成一个无参构造函数,如果写了有参构造函数,那么就不会帮我们生成了,必须自己写一个无惨构造函数,才能直接定义对象。
拷贝构造函数(赋值构造)
-
用一个对象去初始化另一个对象时(函数传参也会拷贝),需要拷贝构造(如果自己没有写,编译器会自动帮我们生成)
Test t(1,"2"); //1,赋值符号 Test t1 =t; //2,参数方法 Test t2(t);t2 = t1; //这个调用的是赋值运算符重载函数
-
注意:定义之后进行赋值不会调用拷贝构造函数,而是调用赋值函数,这是运算符重载,这个涉及到运算符重载的知识,这个我们稍后讲解,注意:拷贝构造与运算符重载很容易搞混
移动构造函数
- 移动构造函数数用来实现移动语义,转移对象之间的资源(如果自己没有写,编译器会自动帮我们生成),调用std::move()
// 定义一个对象
Test t1("wy", 18);
Test t2(std::move(t1)); //移动构造,这个时候对象t1所有权都转移给了t2,t1没有了资源,这样提高了资源的利用率
移动std::move()这个东西,我感觉很神奇,没有结合实践,感觉就这么回事,但是一结合实际,就会发现他特别伟大,特别好用!!!!
深拷贝和浅拷贝
首先,明确一点深拷贝和浅拷贝是针对类里面有指针的对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对于对象来说在进行浅拷贝时,只是将对象的指针复制了一份,也就内存地址,即两个不同的对象里面的指针指向了同一个内存地址,那么在改变任一个对象的指针指向的内存的值时,都是该变这个内存地址的所存储的值,所以两个变量的值都会改变。
简单来说,当数据成员中有指针时,必须要用深拷贝。
- 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址。
- 使用浅拷贝,释放内存的时候可能会出现重复释放同一块内存空间的错误。
- 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
- 使用深拷贝下,释放内存的时候不会因为出现重复释放同一个内存的错误。
注意
- C++类中默认提供的拷贝构造函数,是浅拷贝的
- 要想实现深拷贝,必须自己手动实现拷贝构造函数
//自己实现深拷贝
TString(const TString& other) //普通:右值引用
{if(&other != this) { // 不是自己拷贝自己m_size = other.m_size;m_str = new cahr[m_size + 1];strcay(m_str,other.m_str);}
}int mian()
{TString other = TString hello; //hello 为TString的一个实例化对象
}
构造函数的初始化参数列表
初始化参数列表
当我们再构造函数进行赋值成员变量的时候,可以有以下两种方法:
class Student
{
public:// 方法一Student(std::string uid, std::string name, int age){m_uid = uid;m_name = name;m_age = age;}// 方法二,推荐:参数列表方法,不同变量之间用 ‘,’ 隔开 Student(std::string uid, std::string name, int age):m_uid(uid),m_name(name),m_age(age){}private:std:string m_uid;std::string m_name;int m_age;
}
两种方法都可,但是我比较喜欢第二种。
类中类如何构造
类的组合:组合(有时候叫聚合)是将一个对象放到另一个对象里)。它是一种 has-a 的关系。
简单来说,就是一个类的对象作为另一个类的成员,这就叫做类的组合。
那这个时候这么对每一个对象值赋值呢?
假设我们再一个类B中创建了一个类A作为成员变量,而且A类中成员变量中,它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数
class A
{
public:A(int a) {int m_a = a;}
private:int m_a;
}// 定义类B
class B
{
public:B(int b, int a):a1(a),m_b1 = b{}private:int m_b1;A a1; // 创建A对象
}
本类和对象成都需要执行构造函数,那么谁先执行呢?有什么样的顺序呢?
- 先指针被组合对象的构造函数,如果组合对象有多个,按照定义顺序,而不是按照初始化列表的顺序!
- 析构和构造顺序相反,这个再上面将构造和析构函数有讲解,如果大家忘了,可以回去看一下哦🤠🤠🤠
相关文章:

C/C++语言基础--C++构造函数、析构函数、深拷贝与浅拷贝等等相关知识讲解
本专栏目的 更新C/C的基础语法,包括C的一些新特性 前言 周末休息了,没有更新,请大家见谅哈;构造函数、析构函数可以说便随着C每一个程序,故学构造函数、析构函数是必要的;C语言后面也会继续更新知识点&am…...
json格式互相转换
您提供的字符串已经是一个JSON格式的字符串,但是JSON标准要求键名必须用双引号括起来,而不是单引号。因此,您需要将字符串中的单引号替换为双引号。以下是转换后的JSON字符串: {"图片描述": "高速公路上发生了严重…...
Linux下共享内存详解
共享内存是Linux中一种高效的进程间通信(IPC)方式,它允许多个进程共享同一段内存,从而实现数据的快速传递。共享内存通常比其他IPC机制(如管道或消息队列)更快,因为数据直接存储在内存中&#x…...

MySQL篇(管理工具)
目录 一、系统数据库 二、常用工具 1. mysql 2. mysqladmin 3. mysqlbinlog 4. mysqlshow 5. mysqldump 6. mysqlimport/source 6.1 mysqlimport 6.2 source 一、系统数据库 MySQL数据库安装完成后,自带了一下四个数据库,具体作用如下…...
redis学习笔记(六)
redis每种数据结构的应用场景 1. 字符串 (String) 应用场景 : 缓存:存储频繁访问的数据,如网页缓存、会话信息等。计数器:实现统计和计数功能,如访问计数、统计数据等。键值存储:简单的键值对存储…...
spring与springmvc整合
文章目录 spring与springmvc整合重复创建bean容器关系获取spring容器上下文 spring与springmvc整合 在项目中使用springmvc的时候,由于spring和springmvc是同源的,有时候大家会把所有的配置都扔到springmvc的配置文件中,而不去区分spring和s…...
如何使用Optuna在PyTorch中进行超参数优化
所有神经网络在训练过程中都需要选择超参数,而这些超参数对收敛速度和最终性能有着非常显著的影响。 这些超参数需要特别调整,以充分发挥模型的潜力。超参数调优过程是神经网络训练中不可或缺的一部分,某种程度上,它是一个主要基于梯度优化问题中的“无梯度”部分。 在这…...

2.Spring-容器-注入
注册:将组件放入容器中; 注入:让容器按需进行操作; 一、Autowired:自动注入组件 原理:Spring调用容器的getBean 二、Qualifier 精确指定 精确指定:如果容器中组件存在多个,则使用…...
在uboot中添加自定义命令
有时候为了方便测试,我们需要在Uboot中添加自己的命令,这时可以通过下面的步骤实现: 1、在common目录下添加自己的命令文件“cmd_命令名.c”,如cmd_test.c,内容如下(参考模版): …...
AngularJS 模块
AngularJS 模块 AngularJS,作为一个强大且灵活的前端框架,其核心特性之一就是模块化。模块在AngularJS中扮演着至关重要的角色,它们是组织代码的主要方式,使得开发者能够创建可复用、可维护且易于测试的代码结构。本文将深入探讨AngularJS模块的概念、用途、创建方式以及最…...

[yotroy.cool] MGT 388 - Finance for Engineers - notes 笔记
个人博客https://www.yotroy.cool/,感谢关注~ 图片资源可能显示不全,请前往博客查看哦! ============================================================ Lecture 1 What is Accounting? The process of identifying, measuring and communicating economic informati…...
2024年9月python二级易错题和难题大全(附详细解析)(三)
2024年9月python二级易错题和难题大全(附详细解析)(三) 第1题第2题第3题第4题第5题第6题第7题第8题第9题第10题第11题第12题第13题第14题第15题第16题第17题第18题第19题第20题第1题 1、以下程序的输出结果是() L1 = [4, 5, 6, 8].reverse() print(L1)A、[8, 6, 5, 4]&…...

【LLM多模态】Animatediff文生视频大模型
note AnimateDiff框架:核心是一个可插拔的运动模块,它可以从真实世界视频中学习通用的运动先验,并与任何基于相同基础T2I的个性化模型集成,以生成动画。训练策略:AnimateDiff的训练包括三个阶段: 领域适配…...
PDB数据库中蛋白质结构文件数据格式
在PDB(Protein Data Bank)数据库中,蛋白质结构文件通常以两种主要格式存储:.pdb(PDB格式)和 .cif(CIF格式,Crystallographic Information File)。这两种文件格式记录了蛋白质的三维结构坐标信息以及实验数据,但它们的表达方式和用途有所不同。 1. PDB数据库中的结构…...

C++自动驾驶面试核心问题整理
应用开发 概述:比较基础,没啥壁垒,主要有linux开发经验即可 问题:基础八股,如计算机网络、操作系统、c11等基础三件套;中等难度算法题1-2道。 中间件开发(性能优化) 概述&am…...

2024寻找那些能精准修改PDF内容的工具
如今,我们使用 PDF 文档的频率不断攀升,很多时候收到的表格等资料都是 PDF 格式。若先进行格式转换后编辑,再转换回 PDF 格式,着实有些麻烦。那么,pdf怎么编辑修改内容呢?在这篇文章中,我将为大…...

POI操作EXCEL增加下拉框
文章目录 POI操作EXCEL增加下拉框 POI操作EXCEL增加下拉框 有时候通过excel将数据批量导入到系统,而业务操作人员对于一些列不想手动输入,而是采用下拉框的方式来进行选择 采用隐藏sheet页的方式来进行操作 String sheetName "supplier_hidden_s…...

新手教学系列——基于统一页面的管理后台设计(二)集成篇
在现代企业级应用中,后台管理系统不仅是业务运营的核心,还承担着数据管理、用户权限控制等重要功能。随着业务规模的不断扩大,系统架构逐渐向微服务转变,多个后端服务模块协同工作,如何高效地集成这些模块,确保系统的稳定性和可维护性,成为开发者亟需解决的问题。在《新…...

计算机毕业设计之:基于微信小程序的疫苗预约系统的设计与实现(源码+文档+讲解)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...

Redis事务总结
1.事务介绍 Redis 事务是一个用于将多个命令打包在一起执行的功能,它可以确保这些命令按照顺序执行,并且具有原子性。这意味着事务中的命令要么全部执行,要么全部不执行,这有助于保持数据的一致性。 Redis 事务本质:…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...