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++更多知识。

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

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

相关文章

嵌入式培训之数据结构学习(一)数据结构的基础概念、线性表

一、基础概念 1、数据结构&#xff1a;相互之间存在一种或多种特定关系的数据元素的集合。&#xff08;特定关系有逻辑关系与线性关系&#xff09; &#xff08;1&#xff09;逻辑结构 集合&#xff0c;所有数据在同一个集合中&#xff0c;关系平等&#xff08;数组&#xff…

Android Exoplayer 实现多个音视频文件混合播放以及音轨切换

在之前的文章ExoPlayer中常见MediaSource子类的区别和使用场景中介绍了Exoplayer中各种子MediaSource的使用场景&#xff0c;这篇我们着重详细介绍下实现多路流混合播放的用法。常见的使用场景有&#xff1a;视频文件电影字幕、正片视频广告视频、背景视频背景音乐等。 初始化…

推特逆向算法,推特爬虫,数据分析,推特关键词搜索

祝大家五一假期快乐&#xff01; 最近推特加了逆向&#xff0c;频繁出现404&#xff0c;无法正常抓取数据&#xff0c;这里给出推特逆向的思路及代码&#xff0c;供大家参考学习&#xff01; 本文将介绍如何使用 Python 模拟请求 Twitter 的 GraphQL 接口&#xff0c;结合 re…

图形化编程平台的破局之道:从工具同质化到生态差异化

一、同质化困局的底层逻辑剖析 在全球图形化编程市场中&#xff0c;工具功能趋同已成为行业共识。据 Statista 2024 年数据显示&#xff0c;主流平台的基础功能重合度高达 78%&#xff0c;核心模块&#xff08;如条件判断、循环结构&#xff09;的实现方式高度相似。这种现象的…

【Rust】枚举和模式匹配

目录 枚举和模式匹配枚举的定义Option 枚举控制流运算符 match简洁控制流 if let 枚举和模式匹配 枚举的定义 结构体给予你将字段和数据聚合在一起的方法&#xff0c;像 Rectangle 结构体有 width 和 height 两个字段。而枚举给予你一个途径去声明某个值是一个集合中的一员。…

应急响应靶机——WhereIS?

用户名及密码&#xff1a;zgsf/zgsf 下载资源还有个解题.exe: 1、攻击者的两个ip地址 2、flag1和flag2 3、后门程序进程名称 4、攻击者的提权方式(输入程序名称即可) 之前的命令&#xff1a; 1、攻击者的两个ip地址 先获得root权限&#xff0c;查看一下历史命令记录&#x…

变量函数实战:高保真APP原型“发票页面”动态交互教程

变量函数是高保真交互原型设计中常见的高级交互功能&#xff0c;能够避免重复复制与手动修改页面元素和逻辑标注&#xff0c;让演示更有真实体验感。本文分享一个高保真APP交互原型页面的实操案例&#xff0c;结合原型设计工具中的变量函数与逻辑判断功能&#xff0c;手把手教你…

量子加密通信:守护信息安全的未来之盾

摘要 在数字化时代&#xff0c;信息安全成为全球关注的焦点。传统加密技术面临着被量子计算破解的风险&#xff0c;而量子加密通信作为一种基于量子力学原理的新型加密技术&#xff0c;提供了理论上无条件安全的通信保障。本文将详细介绍量子加密通信的基本原理、技术实现、应用…

《Vue.js》阅读之响应式数据与副作用函数

Vue.js 《Vue.js设计与实现》&#xff08;霍春阳&#xff09; 适合&#xff1a;从零手写Vue3响应式系统&#xff0c;大厂面试源码题直接覆盖。重点章节&#xff1a;第4章&#xff08;响应式&#xff09;、第5章&#xff08;渲染器&#xff09;、第8章&#xff08;编译器&…

数据处理专题(十三)

学会基本的图像处理技术。‍ OpenCV 基础 实践&#xff1a;使用 OpenCV 进行图像读取、显示和基本处理‍ 03 代码示例 1. 导入必要的库 import cv2import numpy as npimport matplotlib.pyplot as plt 2. 图像读取 # 读取图像image_path path_to_your_image.jpg # 替换…

springboot旅游小程序-计算机毕业设计源码76696

目 录 摘要 1 绪论 1.1研究背景与意义 1.2研究现状 1.3论文结构与章节安排 2 基于微信小程序旅游网站系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统…

P1874 快速求和

目录 题目算法标签: 动态规划, 线性 d p dp dp思路代码 题目 P1874 快速求和 算法标签: 动态规划, 线性 d p dp dp 思路 求的是最少组成 n n n的加法次数, 对于当前数字序列可以设计状态表示 f [ i ] [ j ] f[i][j] f[i][j]表示考虑前 i i i个字符, 并且和是 j j j的所有方…

知名人工智能AI培训公开课内训课程培训师培训老师专家咨询顾问唐兴通AI在金融零售制造业医药服务业创新实践应用

AI赋能未来工作&#xff1a;引爆效率与价值创造的实战营 AI驱动的工作革命&#xff1a;从效率提升到价值共创 培训时长&#xff1a; 本课程不仅是AI工具的操作指南&#xff0c;更是面向未来的工作方式升级罗盘。旨在帮助学员系统掌握AI&#xff08;特别是生成式AI/大语言模型…

Linux 内核参数

文章目录 什么是内核参数参数种类配置方式1. 编译内核时配置2. 内核启动时配置3. 内核运行时配置4. 加载内核模块时配置总结 什么是内核参数 内核参数是 Linux 系统中用于控制和调整内核行为的可配置选项。这些参数影响系统的性能、安全性和各种功能特性。 参数种类 大部分参…

pythonocc 拉伸特征

micromamba install -c conda-forge pythonocc-core opencascade.js安装不起来&#xff0c;ai用pythonocc练个手 拉伸线框 线成面 from OCC.Core.gp import gp_Pnt, gp_Dir, gp_Vec from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire f…

Vue.js 页面切换空白与刷新 404 问题深度解析

在使用 Vue.js 开发单页应用 (SPA) 的过程中&#xff0c;开发者经常会遇到两个常见问题&#xff1a;页面切换时出现短暂的空白屏幕&#xff0c;以及刷新页面时返回 404 错误。这两个问题不仅影响用户体验&#xff0c;还可能阻碍项目的正常上线。本文将深入探讨这两个问题的成因…

Go 语言 slice(切片) 的使用

序言 在许多开发语言中&#xff0c;动态数组是必不可少的一个组成部分。在实际的开发中很少会使用到数组&#xff0c;因为对于数组的大小大多数情况下我们是不能事先就确定好的&#xff0c;所以他不够灵活。动态数组通过提供自动扩容的机制&#xff0c;极大地提升了开发效率。这…

Qt5.14.2 链接 MySQL 8.4 遇到的问题

问题一: "Plugin caching_sha2_password could not be loaded: 找不到指定的模块。 Library path is caching_sha2_password.dll QMYSQL: Unable to connect" 解决方法: alter user root@localhost identified with mysql_native_password by root;问题二: ERR…

Docker 部署 - Crawl4AI 文档 (v0.5.x)

Docker 部署 - Crawl4AI 文档 (v0.5.x) 快速入门 &#x1f680; 拉取并运行基础版本&#xff1a; # 不带安全性的基本运行 docker pull unclecode/crawl4ai:basic docker run -p 11235:11235 unclecode/crawl4ai:basic# 带有 API 安全性启用的运行 docker run -p 11235:1123…

开发工具分享: Web前端编码常用的在线编译器

1.OneCompiler 工具网址&#xff1a;https://onecompiler.com/ OneCompiler支持60多种编程语言&#xff0c;在全球有超过1280万用户&#xff0c;让开发者可以轻易实现代码的编写、运行和共享。 OneCompiler的线上调试功能完全免费&#xff0c;对编程语言的覆盖也很全&#x…