C++类与对象(二):六个默认构造函数(一)
在学C语言时,实现栈和队列时容易忘记初始化和销毁,就会造成内存泄漏。而在C++的类中我们忘记写初始化和销毁函数时,编译器会自动生成构造函数和析构函数,对应的初始化和在对象生命周期结束时清理资源。那是什么是默认构造函数呢?例如在一个空类中,编译器会自动生成六个默认函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
目录
构造函数
构造函数的特性:
析构函数
拷贝构造函数
构造函数
概念:是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
构造函数的特性:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。
7、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。
示例:
class Student {
public:Student(){cout << "Student()" << endl;}
private:int age;
};int main()
{Student s1;
}
运行结果:
创建一个Student对象,我们并未调用初始化函数,编译器自动调用了默认构造函数Student(),这个虽然是我们自己写的,但是也是默认构造函数。
注意:无参的构造函数和全缺省函数都可以称为默认构造函数,并且默认构造函数只能有一个。编译器自动生成的也是一个默认构造函数,所以有三个默认构造函数。
那么在没有写构造函数下,编译器自动生成的默认构造函数会对内置类型以及自定义类型初始化吗?
示例:
class Stack {
private:int* _a;int _top;int _capacity;
};class Student {
public:Student(){cout << "Student()" << endl;}
private:int age;Stack s;
};int main()
{Student st1;return 0;
}
通过调试看一下编译器自动生成的默认构造函数是否对 age和s初始化:
从这里可以看出都进行了初始化,其实不然,如果只有内置类型的话就不初始化了,小编用的是VS2019,编译器不同是否初始化也不同,所以在写代码时不能依赖编译器自动生成的默认构造函数,还是自己手动写更好。
这是只用age变量时的结果 :
总结:编译器自动生成的默认构造函数,对于内置类型不做处理 (但是我们可以在声明时给值),而对于自定义类型会去调用它自己的默认构造函数。
所以一般情况下构造函数都需要我们自己写,除非以下两种情况:
1、 内置类型成员都有缺省值,且符合预期值。
2、全是自定义类型成员,且自定义类型成员都有构造函数。
析构函数
概念:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
析构函数特征:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
示例:
class Stack {
public:Stack(){cout << "Stack()" << endl;_a = (int*)malloc(sizeof(int) * 4);if (_a == nullptr){perror("malloc fail");return;}_capacity = 4;_top = 0;}~Stack(){cout << "~Stack" << endl;free(_a);_a = nullptr;_capacity = _top = 0;}
private:int* _a;int _top;int _capacity;
};int main()
{Stack s1;return 0;
}
运行结果:
这里都会自动调用析构函数来清理资源,那么问题来了,是每次都需要写析构函数吗?
析构函数就和之前对动态空间释放一样,对于内置类型变量并没有处理,因为它们在栈上开辟的空间,在程序生命周期结束会自动销毁,而向系统申请的空间不一样,是开辟在堆上的,不能自动释放,只能手动释放空间,所以有以下三种情况:
1、一般情况下,有动态资源申请,就需要显示写析构函数。
2、没有动态资源申请,不需要写析构函数。
3、需要资源释放的都是自定义成员,不要写析构函数。
拷贝构造函数
概念:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。
如果形参是值传递,那么会产生什么结果?
示例:
class Student {
public:Student(int age = 18){cout << "Student()" << endl;_age = age;}Student(const Student s)//报错{_age = s._age;}Student(const Student& s)//正确写法{_age = s._age;}
private:int _age;
};int main()
{Student s1(20);Student s2(s1);return 0;
}
这里会报错,形成无穷递归,传值调用时会先调用Student(Student s),因为传值所以会再一次调用,循环往复,所以报错了,可以通过一下示例来加深理解:
这里进行调试,在准备进入test函数时观察是否进入了test函数:
这里可以观察到在准备进入test函数时却进入Student函数,所以如果在Student函数的形参的传递是值传递,那么就会无穷递归。
C++规定:内置类型可以直接拷贝,自定义类型必须调用拷贝构造完成拷贝。
拷贝构造函数特征:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
对于内置类型的拷贝可以不用写拷贝构造函数,但是对于自定义类型需要自己写拷贝构造函数
示例:
class Stack {
public:Stack(){cout << "Stack()" << endl;_a = (int*)malloc(sizeof(int) * 4);if (_a == nullptr){perror("malloc fail");return;}_capacity = 4;_top = 0;}~Stack(){cout << "~Stack" << endl;free(_a);_a = nullptr;_capacity = _top = 0;}
private:int* _a;int _top;int _capacity;
};int main()
{Stack st1;Stack st2(st1);return 0;
}
这段代码没有写拷贝构造函数,看看会出现什么结果:
这是什么问题呢?看不出可以调试看一下:
通过调试可以看出,两个对象的_a的地址相同,也就是说指向了同一块空间,当指向同一块空间时,会对该空间进行两次析构,所以程序崩溃了。
这时就需要我们自己来写拷贝构造函数,不可以依赖编译器自动生成的拷贝构造函数。
正确代码:
class Stack {
public:Stack(){cout << "Stack()" << endl;_a = (int*)malloc(sizeof(int) * 4);if (_a == nullptr){perror("malloc fail");return;}_capacity = 4;_top = 0;}~Stack(){cout << "~Stack" << endl;free(_a);_a = nullptr;_capacity = _top = 0;}Stack(const Stack& st){_a = (int*)malloc(sizeof(int) * st._top);if (_a == nullptr){perror("malloc fail");return;}_capacity = st._capacity;_top = st._top;}
private:int* _a;int _top;int _capacity;
};int main()
{Stack st1;Stack st2(st1);return 0;
}
运行结果:
这就是自定义类型的深拷贝,当然深拷贝的知识不止这一点点,关注博主后续给你带来C++更多知识。
相关文章:

C++类与对象(二):六个默认构造函数(一)
在学C语言时,实现栈和队列时容易忘记初始化和销毁,就会造成内存泄漏。而在C的类中我们忘记写初始化和销毁函数时,编译器会自动生成构造函数和析构函数,对应的初始化和在对象生命周期结束时清理资源。那是什么是默认构造函数呢&…...
OpenCV CUDA 模块中在 GPU 上对图像或矩阵进行 翻转(镜像)操作的一个函数 flip()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::flip 是 OpenCV 的 CUDA 模块中的一个函数,用于在 GPU 上对图像或矩阵进行 翻转(镜像)操作。它类似…...
基于大模型的原发性醛固酮增多症全流程预测与诊疗方案研究
目录 一、引言 1.1 研究背景与意义 1.2 国内外研究现状 1.3 研究目的与方法 二、原发性醛固酮增多症概述 2.1 疾病定义与发病机制 2.2 临床表现与诊断标准 2.3 流行病学特征 三、大模型预测原理与技术 3.1 大模型简介 3.2 预测原理与算法 3.3 数据收集与预处理 四…...

从逻辑学视角探索数学在数据科学中的系统应用:一个整合框架
声明:一家之言,看个乐子就行。 图表采用了两个维度组织知识结构: 垂直维度:从上到下展示了知识的抽象到具体的演进过程,分为四个主要层级: 逻辑学基础 - 包括数理逻辑框架和证明理论数学基础结构 - 涵盖…...

Matplotlib 完全指南:从入门到精通
前言 Matplotlib 是 Python 中最基础、最强大的数据可视化库之一。无论你是数据分析师、数据科学家还是研究人员,掌握 Matplotlib 都是必不可少的技能。本文将带你从零开始学习 Matplotlib,帮助你掌握各种图表的绘制方法和高级技巧。 目录 Matplotli…...

如何有效追踪需求的实现情况
有效追踪需求实现情况,需要清晰的需求定义、高效的需求跟踪工具、持续的沟通反馈机制,其中高效的需求跟踪工具尤为关键。 使用需求跟踪工具能确保需求实现进度可视化、提高团队协作效率,并帮助识别和管理潜在风险。例如,使用专业的…...

自动驾驶技术栈——DoIP通信协议
一、DoIP协议简介 DoIP,英文全称是Diagnostic communication over Internet Protocol,是一种基于因特网的诊断通信协议。 DoIP协议基于TCP/IP等网络协议实现了车辆电子控制单元(ECU)与诊断应用程序之间的通信,常用于汽车行业的远程诊断、远…...

C++ 与 Go、Rust、C#:基于实践场景的语言特性对比
目录 编辑 一、语法特性对比 1.1 变量声明与数据类型 1.2 函数与控制流 1.3 面向对象特性 二、性能表现对比编辑 2.1 基准测试数据 在计算密集型任务(如 10⁷ 次加法运算)中: 在内存分配测试(10⁵ 次对象创建…...
Docker 中的 DNS 解析机制
在 Docker 容器化环境中,网络连接是至关重要的,而 DNS(Domain Name System,域名系统)解析则是网络通信的基础。容器需要能够解析内部服务名称以及外部域名,以便与其他容器或外部世界进行交互。理解 Docker 如何处理 DNS 请求,可以帮助我们更好地配置和排查网络问题。 D…...
数字化工厂中央控制室驾驶舱系统 API接口文档
数字化工厂中央控制室驾驶舱系统 API接口文档 本文档详细描述了数字化工厂中央控制室驾驶舱系统的API接口规范,包括中端服务提供的数据接口和算法接口。 1. 通用规范 1.1 基础URL 后端服务: http://localhost:8000中端服务数据API: http://localhost:8001中端服…...

如何更改默认字体:ONLYOFFICE 协作空间、桌面编辑器、文档测试示例
在处理办公文件时,字体对提升用户体验至关重要。本文将逐步指导您如何在 ONLYOFFICE 协作空间、桌面应用及文档测试示例中自定义默认字体,以满足个性化需求,更好地掌控文档样式。 关于 ONLYOFFICE ONLYOFFICE 是一个国际开源项目,…...

设计模式之工厂模式(二):实际案例
设计模式之工厂模式(一) 在阅读Qt网络部分源码时候,发现在某处运用了工厂模式,而且编程技巧也用的好,于是就想分享出来,供大家参考,理解的不对的地方请多多指点。 以下是我整理出来的类图: 关键说明&#x…...

基于VeRL源码深度拆解字节Seed的DAPO
1. 背景与现状:从PPO到GRPO的技术演进 1.1 PPO算法的基础与局限 Proximal Policy Optimization(PPO)作为当前强化学习领域的主流算法,通过重要性采样比率剪裁机制将策略更新限制在先前策略的近端区域内,构建了稳定的…...

zst-2001 历年真题 软件工程
软件工程 - 第1题 b 软件工程 - 第2题 c 软件工程 - 第3题 c 软件工程 - 第4题 b 软件工程 - 第5题 b 软件工程 - 第6题 0.未完成:未执行未得到目标。1.已执行:输入-输出实现支持2.已管理:过程制度化,项目遵…...
WSL 安装 Debian 12 后,Linux 如何安装 redis ?
在 WSL 的 Debian 12 上安装 Redis 的步骤如下: 1. 更新系统包列表 sudo apt update && sudo apt upgrade -y2. 安装 Redis sudo apt install redis-server -y3. 启动 Redis 服务 sudo systemctl start redis4. 设置开机自启 sudo systemctl enable red…...
在Ubuntu系统下编译OpenCV 4.8源码
编译OpenCV 4.8源码可以为你提供更高的灵活性和优化性能,适合特定的需求。以下是详细的步骤,指导你在Ubuntu系统上编译和安装OpenCV 4.8。 1. 安装必要的依赖 首先,确保你的系统已经安装了所有必要的依赖项。 sudo apt update sudo apt in…...
Arduino快速入门
Arduino快速入门指南 一、硬件准备 选择开发板: 推荐使用 Arduino UNO(兼容性强,适合初学者),其他常见型号包括NANO(体积小)、Mega(接口更多)。准备基础元件:…...

基于WSL用MSVC编译ffmpeg7.1
在windows平台编译FFmpeg,网上的大部分资料都是推荐用msys2mingw进行编译。在win10平台,我们可以采用另一种方式,即wslmsvc 实现window平台的ffmpeg编译。 下面将以vs2022ubuntu22.04 为例,介绍此方法 0、前期准备 安装vs2022 &…...

java命令行打包class为jar并运行
1.创建无包名类: 2.添加依赖jackson 3.引用依赖包 4.命令编译class文件 生成命令: javac -d out -classpath lib/jackson-core-2.13.3.jar:lib/jackson-annotations-2.13.3.jar:lib/jackson-databind-2.13.3.jar src/UdpServer.java 编译生成class文件如下 <...

vue注册用户使用v-model实现数据双向绑定
定义数据模型 Login.vue //定义数据模型 const registerData ref({username: ,password: ,confirmPassword: })使用 v-model 实现数据模型的key与注册表单中的元素之间的双向绑定 <!-- 注册表单 --><el-form ref"form" size"large" autocompl…...
Android学习之响应式编程
本篇基于DeepSeek 搜索结果修改。 一、响应式编程基础认知 1.1 为什么需要响应式编程? 在传统的Android开发中,我们经常会遇到以下痛点: // 传统方式处理数据变化 button.setOnClickListener {// 触发网络请求fetchDataFromNetwork { res…...
如何使用极狐GitLab 软件包仓库功能托管 helm chart?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 软件包库中的 Helm charts (BASIC ALL) WARNING:Helm chart 库正在开发中,由于功能有限,尚未准备好用…...
中国古代史4
东汉 公元25年,刘秀建立东汉,定都洛阳,史称光武中兴 白马寺:汉明帝时期建立,是佛教传入中国后兴建的第一座官办寺院,有中国佛教的“祖庭”和“释源”之称,距今1900多年历史 班超—西域都护—投…...

Nacos源码—8.Nacos升级gRPC分析六
大纲 7.服务端对服务实例进行健康检查 8.服务下线如何注销注册表和客户端等信息 9.事件驱动架构源码分析 一.处理ClientChangedEvent事件 也就是同步数据到集群节点: public class DistroClientDataProcessor extends SmartSubscriber implements DistroDataSt…...
基于Vue3.0的高德地图api教程005:实现绘制线并编辑功能
文章目录 6、绘制多段线6.1 绘制多段线6.1.1 开启绘制功能6.1.2 双击完成绘制6.1.3 保存到数据库6.2 修改多段线6.2.1 点击线,进入编辑模式6.2.2 编辑线6.3 完整代码6、绘制多段线 6.1 绘制多段线 6.1.1 开启绘制功能 实现代码: const changeSwitchDrawPolyline = ()=>…...

SpringBoot 自动装配原理 自定义一个 starter
目录 1、pom.xml 文件1.1、parent 模块1.1.1、资源文件1.1.1.1、resources 标签说明1.1.1.2、从 Maven 视角:资源处理全流程 1.1.2、插件 1.2、dependencies 模块 2、启动器3、主程序3.1、SpringBootApplication 注解3.2、SpringBootConfiguration 注解3.2.1、Con…...

【C++进阶篇】多态
深入探索C多态:静态与动态绑定的奥秘 一. 多态1.1 定义1.2 多态定义及实现1.2.1 多态构成条件1.2.1.1 实现多态两个必要条件1.2.1.2 虚函数1.2.1.3 虚函数的重写/覆盖1.2.1.4 协变1.2.1.5 析构函数重写1.2.1.6 override和final关键字1.2.1.7 重载/重写/隐藏的对⽐ 1…...
Redis 基础详解:从入门到精通
在当今互联网应用开发领域,数据存储与处理的性能和效率至关重要。Redis(Remote Dictionary Server)作为一款开源的、基于内存的键值存储系统,凭借其出色的性能和丰富的功能,被广泛应用于数据库、缓存、消息中间件等场景…...
Android Studio的jks文件
在 Android Studio 中,.jks 文件是 Java KeyStore(Java 密钥库)文件的一种,用于存储和管理用于签署 Android 应用程序的数字证书和私钥。 一、.jks 文件的作用 在 Android 开发中,.jks 文件主要用于: 应用…...
互联网大厂Java面试实战:从Spring Boot到微服务的技术问答与解析
💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通 😁 2. 毕业设计专栏,毕业季咱们不慌忙,几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…...