当前位置: 首页 > news >正文

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的基础语法&#xff0c;包括C的一些新特性 前言 周末休息了&#xff0c;没有更新&#xff0c;请大家见谅哈&#xff1b;构造函数、析构函数可以说便随着C每一个程序&#xff0c;故学构造函数、析构函数是必要的&#xff1b;C语言后面也会继续更新知识点&am…...

json格式互相转换

您提供的字符串已经是一个JSON格式的字符串&#xff0c;但是JSON标准要求键名必须用双引号括起来&#xff0c;而不是单引号。因此&#xff0c;您需要将字符串中的单引号替换为双引号。以下是转换后的JSON字符串&#xff1a; {"图片描述": "高速公路上发生了严重…...

Linux下共享内存详解

共享内存是Linux中一种高效的进程间通信&#xff08;IPC&#xff09;方式&#xff0c;它允许多个进程共享同一段内存&#xff0c;从而实现数据的快速传递。共享内存通常比其他IPC机制&#xff08;如管道或消息队列&#xff09;更快&#xff0c;因为数据直接存储在内存中&#x…...

MySQL篇(管理工具)

目录 一、系统数据库 二、常用工具 1. mysql 2. mysqladmin 3. mysqlbinlog 4. mysqlshow 5. mysqldump 6. mysqlimport/source 6.1 mysqlimport 6.2 source 一、系统数据库 MySQL数据库安装完成后&#xff0c;自带了一下四个数据库&#xff0c;具体作用如下&#xf…...

redis学习笔记(六)

redis每种数据结构的应用场景 1. 字符串 (String) 应用场景 &#xff1a; 缓存&#xff1a;存储频繁访问的数据&#xff0c;如网页缓存、会话信息等。计数器&#xff1a;实现统计和计数功能&#xff0c;如访问计数、统计数据等。键值存储&#xff1a;简单的键值对存储&#xf…...

spring与springmvc整合

文章目录 spring与springmvc整合重复创建bean容器关系获取spring容器上下文 spring与springmvc整合 在项目中使用springmvc的时候&#xff0c;由于spring和springmvc是同源的&#xff0c;有时候大家会把所有的配置都扔到springmvc的配置文件中&#xff0c;而不去区分spring和s…...

如何使用Optuna在PyTorch中进行超参数优化

所有神经网络在训练过程中都需要选择超参数,而这些超参数对收敛速度和最终性能有着非常显著的影响。 这些超参数需要特别调整,以充分发挥模型的潜力。超参数调优过程是神经网络训练中不可或缺的一部分,某种程度上,它是一个主要基于梯度优化问题中的“无梯度”部分。 在这…...

2.Spring-容器-注入

注册&#xff1a;将组件放入容器中&#xff1b; 注入&#xff1a;让容器按需进行操作&#xff1b; 一、Autowired&#xff1a;自动注入组件 原理&#xff1a;Spring调用容器的getBean 二、Qualifier 精确指定 精确指定&#xff1a;如果容器中组件存在多个&#xff0c;则使用…...

在uboot中添加自定义命令

有时候为了方便测试&#xff0c;我们需要在Uboot中添加自己的命令&#xff0c;这时可以通过下面的步骤实现&#xff1a; 1、在common目录下添加自己的命令文件“cmd_命令名.c”&#xff0c;如cmd_test.c&#xff0c;内容如下&#xff08;参考模版&#xff09;&#xff1a; …...

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框架&#xff1a;核心是一个可插拔的运动模块&#xff0c;它可以从真实世界视频中学习通用的运动先验&#xff0c;并与任何基于相同基础T2I的个性化模型集成&#xff0c;以生成动画。训练策略&#xff1a;AnimateDiff的训练包括三个阶段&#xff1a; 领域适配…...

PDB数据库中蛋白质结构文件数据格式

在PDB(Protein Data Bank)数据库中,蛋白质结构文件通常以两种主要格式存储:.pdb(PDB格式)和 .cif(CIF格式,Crystallographic Information File)。这两种文件格式记录了蛋白质的三维结构坐标信息以及实验数据,但它们的表达方式和用途有所不同。 1. PDB数据库中的结构…...

C++自动驾驶面试核心问题整理

应用开发 概述&#xff1a;比较基础&#xff0c;没啥壁垒&#xff0c;主要有linux开发经验即可 问题&#xff1a;基础八股&#xff0c;如计算机网络、操作系统、c11等基础三件套&#xff1b;中等难度算法题1-2道。 中间件开发&#xff08;性能优化&#xff09; 概述&am…...

2024寻找那些能精准修改PDF内容的工具

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

POI操作EXCEL增加下拉框

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

新手教学系列——基于统一页面的管理后台设计(二)集成篇

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

计算机毕业设计之:基于微信小程序的疫苗预约系统的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…...

Redis事务总结

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

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...