C++类和对象中(构造函数,析构函数,拷贝构造函数)详解
C++类和对象中[构造函数,析构函数,拷贝构造函数]详解
- 一.前言
- 1.类的6个默认成员函数
- 二.构造函数
- 1.构造函数的引出
- 2.无参构造函数
- 3.缺省参数在构造函数中的应用
- 4.编译器实现的默认构造函数
- 5.广义的默认构造函数
- 6.默认构造函数的形成规则
- 三.析构函数
- 1.析构函数的语法
- 2.编译器实现的默认析构函数
- 四.拷贝构造函数
- 1.拷贝构造函数的引出
- 1.浅拷贝的局限性
- 2.拷贝构造函数语法形式
- 1.浅拷贝的另一大坏处:程序死递归(以date类为例引出)
- 2.日期类的拷贝构造函数的实现
- 3.Stack类的拷贝构造函数的实现
- 1.思路
- 2.具体实现
- 4.指针作为拷贝构造函数的参数
- 5.const Date& d1
- 3.拷贝构造函数的"传递性"
- 4.拷贝构造函数典型调用场景
- 5.拷贝构造函数的总结
一.前言
1.类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,当一个类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
这篇博客我们重点介绍构造函数和析构函数还有拷贝构造函数
这里请大家先建立一个观念:
构造函数和析构函数是函数中的贵族,我们不能用普通函数的视角去看待它们,而拷贝构造函数是构造函数中的一种
它们是非常独特的函数
二.构造函数
1.构造函数的引出
注意:构造函数虽然叫做构造函数,但是它的任务并不是开辟空间去创建一个对象,而是初始化一个对象
这里的构造函数是:
Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
注意:构造函数没有返回值,不写返回值类型,不写void
你发现没有,这个Date的构造函数跟Init的实现一样啊,这也就说明了构造函数的功能就是初始化对象,不是开辟空间创建一个对象
那它为什么不叫作初始化函数呢?C++创始人就是这么定义的,没有为什么
构造函数是自动调用的,不过我们在构造函数中写了三个形参,所以即使构造函数是自动调用的,我们也要去传参啊,怎么传参呢?
C++语法这么规定:在创建对象的时候直接一起传参
Date d1(2023,10,20);
2.无参构造函数
这不还是需要传参吗?
如果我在创建对象的时候都忘了传参呢?
因为构造函数是支持重载的
所以我们可以再去定义一个无参构造函数
Date()
{_year = 1;_month = 2;_day = 3;
}
调用:Date d2;
注意:调用无参构造函数的时候不能加()
也就是不允许这样去调用:
Date d2();
为什么呢?
因为这行代码也可以被认为是一个函数的声明,这个函数的函数名是d2,函数的返回值类型是Date类型,函数没有任何形参
所以会产生歧义
因此C++创始人就规定不要加括号,直接Date d2;就行
3.缺省参数在构造函数中的应用
还是有点麻烦啊,我想去随心所欲地创建一个对象
就像这样:我还要定义这么两个构造函数,
能不能就只定义一个构造函数,而且还能让我更加随心所欲地去传参呢?
int main()
{Date d1(2023, 10, 20);d1.Print();Date d2;d2.Print();return 0;
}
答案是:当然可以了,我们之前学的缺省参数就派上用场了
然后我们写完之后迫不及待地去运行,结果:报错…
为什么呢?
两个报错,
1.其实这个全缺省参数的构造函数跟
Date(int year,int month,int day);
无法构成重载,(因为函数名相同,参数的个数,类型均相同)
2.这个全缺省参数的构造函数尽管与无参构造函数形成了重载,但是当我们无参调用的时候就会发生歧义,因此这两个构造函数也不能同时存在
不错,达到了我们的要求
但是有一个问题:当我们不写构造函数的时候会发生什么呢?
4.编译器实现的默认构造函数
这里所提到的默认构造函数是编译器为我们实现的默认构造函数(大家可以理解为这里是狭义的默认构造函数,后面我们会提到广义的默认构造函数)
编译器会给我们实现一个默认构造函数,不过它会给我们初始化为随机值,
那我要它有什么用?
别急,我们先来看一个代码
这不还是没有什么好的初始化吗?
还是随机值啊
大家不要急吗,下面才是重头戏
我们先给Stack这个类写上我们定义的构造函数
Stack(int capacity = 4)
{_a = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _a){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_top = 0;
}
注意:如果这里的Stack没有我们自定义的构造函数,那么Stack就会调用Stack的默认构造函数(编译器生成的默认构造函数),也就是初始化为随机值
所以:
如果一个类中有自定义类型的成员变量,
那么我们可以考虑使用这个类的默认构造函数(编译器给我们生成的默认构造函数)
既然你去使用这个类的默认构造函数,那么这个类中的内置类型的成员变量怎么办呢?
C++11支持我们去在类的成员变量当中给缺省值
class MyQueue
{
private:Stack pushst;Stack popst;int _size = 1;这个依然是声明,只不过是给了一个缺省值而已永远记住:对于非函数来说,到底是声明还是定义要看是否真的开辟了空间函数的话看什么声明还是定义就很简单了吧
};
在VS2019中,编译器给我们处理了,而在VS2013中编译器没有给我们去处理
总结:
1.一般情况下,我们都要自己写构造函数
2.成员都是自定义类型,或者声明时给了缺省值:可以考虑让编译器自己生成构造函数
5.广义的默认构造函数
上面第四点提到的都是狭义的默认构造函数(有一个地方提到了广义的默认构造函数,我在那个地方已经标明了)
下面我们来看看广义的默认构造函数是什么?
这个初始化列表我们后面会进行详细介绍的
6.默认构造函数的形成规则
那么在什么情况下编译器才会给我们自动去生成一个无参的默认构造函数呢?
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
当我们调用默认构造函数时发现不存在默认构造函数,这也就说明了上面那个规则
三.析构函数
1.析构函数的语法
析构函数是非常有价值的,回想一下我们在用C语言去实现数据结构并且使用的时候,
是不是经常很容易就会忘了调用destroy销毁函数,所以析构函数是很有价值的
我们以日期类为例,熟悉一下析构函数的语法
class Date
{
public:Date(int year = 1, int month = 2, int day = 3){_year = year;_month = month;_day = day;}~Date(){cout << "~Date4()" << endl;}void Print(){cout << _year << " " << _month << " " << _day << endl;}
private:int _year = 0;int _month = 0;int _day = 0;
};
因此:
如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;
有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
2.编译器实现的默认析构函数
当我们不写析构函数的时候,会发生什么现象呢?
默认生成的析构函数,行为跟构造函数类似
内置类型成员不做处理
自定义类型回去调用它的析构
四.拷贝构造函数
1.拷贝构造函数的引出
在C++中结构体扩展为了类,所以对于类而言,它同时也包含了许多结构体的属性
1.浅拷贝的局限性
下面先请大家看一下这个代码
这里我们对Date类进行了传值传参,
而这个传值传参:形参d是实参d1的一份拷贝,d拷贝了d1的所有数据,这个过程是采用值拷贝的方式进行的
而这个代码运行完全正常,别急,下面请大家再看一份代码
我们给Stack函数定义了构造函数和析构函数(这份代码中这两个函数都没有任何问题
但是程序还是崩了,这是为什么呢?
这里Func2函数的形参st是实参st1的一份拷贝,这里的拷贝依然是采用值拷贝的方式来进行的
也就是说:
这种编译器默认形成的采用值拷贝的方式来进行拷贝构造的方式,我们称之为浅拷贝
然后我们再来仔细地看一下上面那个报错的代码
结合这个控制台打印出的信息和上面画的那一张分析图片,
我们再来调试看一下这个过程
这里的st和st1的_a的值是一样的:
当_a指向的空间随着形参st的销毁被释放后,实参st1的_a依然是指向这个已经被释放了的空间的,也就是说这个st1的_a已经成为了一个野指针,因此发生了同一空间多次释放的问题:
我们就能更好地理解浅拷贝的局限性了
也正是因为这种局限性,
对于日期类这种没有在堆上去申请空间的类来说值拷贝没有报错的风险,
但是对于Stack这种在堆上申请了空间的类来说,值拷贝有着致命的缺陷
怎么解决呢?
这就需要我们对Stack类自己写一个拷贝构造函数了,我们要怎么写呢?
这就涉及到深拷贝的知识点了,不要急,我们先来实现一下Date类的拷贝构造函数
2.拷贝构造函数语法形式
这个无穷递归调用是怎么一回事?
下面就会给大家说明
1.浅拷贝的另一大坏处:程序死递归(以date类为例引出)
拷贝构造函数:顾名思义,就是用来拷贝一个对象的所有属性的,
所以不难写出下面的代码
可是编译器直接给我们报错了
这是为什么呢?
这就是无穷递归所引发的错误,为什么会无穷递归呢?
2.日期类的拷贝构造函数的实现
既然传值不行,那就传引用试试
毕竟引用就是取别名,不需要进行值拷贝,在语法上引用作为形参,这个形参就是实参
不难写出这样的代码:
Date(Date& dd){_year = dd._year;_month = dd._month;_day = dd._day;}
传引用就非常好地解决了这个无限递归的问题,
其实我们在这里实现的拷贝构造函数是浅拷贝,但是对于Date类来说,浅拷贝没有危害(因为Date类并没有在堆上开辟空间)
下面我们就去实现Stack类的拷贝构造函数
3.Stack类的拷贝构造函数的实现
注意:我们在这里就要去实现深拷贝了
因为浅拷贝无法解决Stack类的同一空间多次释放的错误
那么怎么去实现呢?
1.思路
2.具体实现
Stack(Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == nullptr){perror("malloc fail");exit(-1);}memcpy(_a, st._a, sizeof(int) * st._top);_top = st._top;_capacity = st._capacity;}
我们一上来遇到的问题就成功解决了
顺便祝大家1024节日快乐
那我们直接试试拷贝构造函数呗
调试看了一下,st1和st2指向的空间的确是不一样的
4.指针作为拷贝构造函数的参数
经过了上面的探索,我们发现引用作为拷贝构造函数的参数是真的好用
那么我用指针行吗
当然可以
不过就是没有引用更加简洁
我们以Date类为例,来看一下
Date(Date* dd){_year = dd->_year;_month = dd->_month;_day = dd->_day;}调用方式:Date d2(&d1);
行是行,不过还是没有引用简洁啊~
5.const Date& d1
如果有人写代码写着写着写懵了,写出了这样的代码
Date1(Date1& dd){dd._year = _year;//这里写反了_month = dd._month;_day = dd._day;}
这就坑了
我想要给d1拷贝一份数据命名为d2,结果年份那里我非但没有拷贝成功,还把我自己给改了
怎么办呢?
因此,C++语法建议在拷贝构造函数的参数那里这么定义:
Date1(const Date1& dd){dd._year = _year;//这里就会报错了_month = dd._month;_day = dd._day;}
然后这个懵了的程序员就意识到问题所在了,然后就能轻而易举地将代码修改正确
3.拷贝构造函数的"传递性"
既然拷贝构造函数是一种特殊的构造函数
那么拷贝构造函数会跟构造函数一样具有"传递性"吗?
答案是:是的.
4.拷贝构造函数典型调用场景
Stack func1()
{Stack st;return st;//这里st也要调用一个拷贝构造函数生成一个临时拷贝,返回临时拷贝
}
//引用是C++入门最重要的知识
Stack& func2()
{static Stack st;return st;//static 修饰,直接返回引用就行
}
5.拷贝构造函数的总结
那么我们什么时候适合需要自己去写拷贝构造函数,什么时候适合直接用编译器默认生成的拷贝构造函数呢?
以上就是C++类和对象中(构造函数,析构函数,拷贝构造函数)详解的全部内容,希望能对大家有所帮助!
相关文章:

C++类和对象中(构造函数,析构函数,拷贝构造函数)详解
C类和对象中[构造函数,析构函数,拷贝构造函数]详解 一.前言1.类的6个默认成员函数 二.构造函数1.构造函数的引出2.无参构造函数3.缺省参数在构造函数中的应用4.编译器实现的默认构造函数5.广义的默认构造函数6.默认构造函数的形成规则 三.析构函数1.析构函数的语法2.编译器实现…...

智能矩阵系统解决的问题?
智能矩阵系统可以解决的问题多种多样,它主要通过人工智能技术应用于矩阵系统,解决一些传统方法难以处理的问题。 以下是一些常见的应用场景: 1. 数据管理:智能矩阵系统可以有效地管理大量的数据,包括数据的存储、检索…...

计算机网络——计算机网络体系结构(3/4)-计算机网络体系结构分层思想举例
目录 发送请求报文 应用层构建HTTP请求报文 运输层添加TCP首部 网络层添加IP首部 数据链路层形成帧 物理层转化为比特流 路由器处理 服务器处理 发回响应报文 计算机网络体系结构分层思想举例 假设网络拓扑如下所示,主机属于网络N1,Web服务器属…...

计算机网络,网络(OSI)七层模型,三次握手四次挥手,get与post请求区别,网络IO(BIO\NIO\AIO),TCP与UDP区别
1.OSI模型? 开放式系统互联通信参考模型(Open System Interconnection Reference Model) OSI网络七层模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 TCP/IP协议群简化了OSI七层模型:应用层、传输层、网络层、数据链路…...

【网络爬虫 | Python】数字货币ok链上bitcoin大额交易实时爬取,存入 mysql 数据库
文章目录 一、网站分析二、js 逆向获取 X-Apikey三、python 调用 js 获取 X-Apikey四、python 爬虫部分五、mysql 数据库、日志、配置文件、目录结构六、结尾 一、网站分析 oklink:https://www.oklink.com/ btc 大额交易:https://www.oklink.com/btc/tx-…...

【Servlet】实现Servlet程序
文章目录 1. 最朴素方式1. 创建项目2. 引入依赖3. 创建目录4. 编写代码5. 打包程序6. 部署程序7. 验证程序 2. 更方便方式1. 安装Smart TomCat插件2. 启动 1. 最朴素方式 1. 创建项目 选择Maven项目 2. 引入依赖 Maven项目创建完后会生成一个pom.xml文件,我们可…...

binlog 和 redolog 有什么区别
binlog 和 redolog 都是 Mysql 里面用来记录数据库数据变更操作的日志. binlog 其中 binlog 主要用来做数据备份、数据恢复和数据同步,在Mysql 的主从数据同步的场景中,master 节点的数据变更,会写入到 binlog 中,然后再把 binl…...
Git 修改已提交的用户名和邮箱
Git 修改已提交的用户名和邮箱 修改上一次提交的邮箱和用户名 git commit --amend --author Name<email>批量修改多次提交的邮箱和用户名 新建一个 .sh 脚本在 git 根目录下.sh脚本内容如下 git filter-branch --env-filter an"$GIT_AUTHOR_NAME" am"…...

小游戏外包开发流程及费用
小游戏的开发流程和费用会因项目的规模、复杂性和所选技术平台而有所不同。以下是一般的小游戏开发流程和可能的费用因素,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 开发流程: 概念和…...

Homeassistant docker配置
Homeassistant docker配置 【说明】本系列为自用教程,记录以便下次使用 【背景】一台J1900 4G64G的小主机,安装了OP系统,里面自带了Docker。为实现Homeassistant(简称HA)控制智能家居设备,进行如下配置。 【…...
Go 深入解析非类型安全指针
一、引言 非类型安全指针(也称为“裸指针”或“原始指针”)在编程领域中一直是一个具有争议和挑战性的主题。它们赋予程序员直接操作计算机内存的能力,为高级性能优化和底层系统交互提供了可能。然而,这种能力往往伴随着高风险&a…...
vue动态绑定class
Vue.js 允许您使用 v-bind 指令或简写的 : 来动态绑定 class 属性。这允许您基于某些条件为元素添加或删除类名,从而实现动态样式控制。以下是一些示例: 动态添加单个类名: <template> <div> <p :class"{ active: isActi…...

UDP网络通信反复发收
package UDP2;import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner;/* * 完成UDP 通信快速入门 实现发1收1*/ public class Client {public static void main(String[] args) throws Exception{// …...

ip报头和ip报文切片组装问题
在tcp层将数据打包封装向下传递后,网络层将其整个看为一个数据,然后对其数据加网络报头操作,在网络层最具有代表的协议就是ip协议。在这里我们探究ipv4的报头。 ip报头 4位版本:指定ip的版本号,对于ipv4来说就是4。 …...

linux之应用编程回顾总结
gcc编译过程 一个c/c文件要经过预处理、编译、汇编和链接4个阶段,才能变成可执行文件 1.预处理 C/C源文件中,以“#”开头的命令被称为预处理命令,如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理…...

nginx配置负载均衡--实战项目(适用于轮询、加权轮询、ip_hash)
👨🎓博主简介 🏅云计算领域优质创作者 🏅华为云开发者社区专家博主 🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…...
Mac GPU MPS常用方法
Requirements Mac computers with Apple silicon or AMD GPUs macOS 12.3 or later Python 3.7 or later Xcode command-line tools: xcode-select --install 判断是否可用 import torch if torch.backends.mps.is_available():mps_device torch.device("mps")x …...

【数据结构】线性表(四)双向链表的各种操作(插入、删除、查找、修改、遍历打印)
目录 线性表的定义及其基本操作(顺序表插入、删除、查找、修改) 四、线性表的链接存储结构 1. 单链表 2. 循环链表 3. 双向链表 a. 双向链表节点结构 b. 创建一个新的节点 c. 在链表末尾插入节点 d. 在指定位置插入节点 e. 删除指定位置的节点…...

数据结构和算法——图
图 有向图 带权图 邻接矩阵 邻接表相较于邻接矩阵,减少了存储空间; 邻接表 参考视频:【尚硅谷】数据结构与算法(Java数据结构与算法)_哔哩哔哩_bilibili...
大数据学习(16)-mapreduce详解
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...