C++ 类和对象(中)构造函数 和 析构函数

news/2024/4/25 12:36:24/文章来源:https://blog.csdn.net/chihiro1122/article/details/130328519

 上篇链接:C++ 类和对象(上)_chihiro1122的博客-CSDN博客

类的6个默认成员函数

 我们在C当中,在写一些函数的时候,比如在栈的例子:

如上述例子,用C++ 返回这个栈是否为空,直接返回的话,这栈空间没有被释放,就会内存泄漏,如果我们直接释放这个栈,那么我们就拿不到这个栈是否为空的 bool 类型值。那么我们在C中就是用 一个 bool 类型变量来接收这函数返回的布尔值 ,然后再释放,最后返回这个 bool 类型变量的值。

这样做非常的麻烦,C++的祖师爷肯定也想到的这些,这就有了类当中的  6 个默认成员函数。

 我们在 上篇中提到了 空类,那么空类当中真的是什么都不存储吗,并不是。其实在空类当中,编译器会帮我们自动的去创造  6  个默认成员函数:

 其中的 构造函数和析构函数就可以帮我们自动进行 一些 如上述的初始化和 销毁的操作。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

 构造函数

构造函数是一个特殊的函数,它的名字和类的名字相同而且在创建类的类型对象的时候调用,一保证每一个成员都有一个初始值,并且,构造函数在对象的整个生命周期当中值调用一次

 特性

 需要注意的是,虽然构造函数是帮助对象当中的成员初始化,但是仅仅只是初始化,并不是给这个成员创建空间。

 其特性如下:

  • 构造函数名和类名一样
  • 没有返回值,也不需要 如 void 这样规定返回值类型
  • 实例化对象时编译器会自动的调用构造函数
  • 构造函数可以重载,也就是说我们可以创建多个构造函数。

 定义构造函数

 我们以上篇中的 栈的初始化来举例子,如果我们要用函数来实现栈的初始化的话,应该这样写:

void StackInit(Stack* ps)
{assert(ps);ps->array = (DataType*)malloc(sizeof(DataType) * 3);if (NULL == ps->array){assert(0);return;}ps->capacity = 3;ps->size = 0;
}

如果我们用构造函数来实现话,就这样来实现:

class Stack
{public:Stack(int capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * 3);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = 3;_size = 0;}
};    

 我们就可以直接把在C++当中 实现的 初始化函数 copy 在这个构造函数中。

这个构造函数在创建 对象的时候就可以自动的去调用,也就是说,可以帮我们在创建对象的时候自动的去 初始化栈。这样我们在使用 这个栈的时候就不需要再去初始化一遍栈了。

 而且一般构造函数都是public的,如果是private 私有的就会报错:

当然我们上述说的是一般,也就是说不是必须的,构造函数也可以是 私有的,但是这是比较 高级的玩法。

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。

 如果我们在类当中没有 定义 构造函数,那么编译器自动的的给我们创建一个 无参数的默认构造函数。

   在C++当中分为基本类型 / 内置类型,这些是语言本身定义的类型,如  int / double / char / 指针;自定义类型,如 用 class / struct 等等来创建的类型。

如果我们不写构造函数的定义,编译器会默认生成构造函数,内置类型不做处理,自定义类型会去调用他的默认构造:

class A
{
public:void Print(){cout << _a << " >>" << _b << " >> " << _c << endl;}protected:int _a;int _b;int _c;
};int main()
{A a;a.Print();return 0;
}

 输出:

 我们发现没有初始化为0,这是在C++当中的语法规定的,但是现在有些新的编译器就会把这个成员默认值初始化为 0。 像VS2019 在类当中 如果在类当中定义了自定义类型的成员,那么其中的内置类型和自定义类型都会进行处理。但是 在 VS2013  就会处理。

 也就是说,内置类型处不处理是编译器个性化的行为,但是在C++当中是规定 内置类型是不进行处理的,我们要默认认为他是不处理的。

 所以,让编译器自己生成构造函数这种操作我们是不建议的,因为指不定这个编译器既不会对 内置类型进行 初始化0,所以,如果类当中有内置成员,我们就要自己实现构造函数来初始化 类当中的 内置成员。

 如果全部都是自定义类型的成员,可以考虑让编译器自己生成。

 类当中的成员的缺省值

 注意:在C++11 当中,C++的开发团队也觉得,内置成员不处理,自定义成员处理,这样当时不妥当,所以大了一个补丁,不是把之前的语法改了,让函数在声明的时候可以给缺省值。

class A
{
public:void Print(){cout << _a << " >>" << _b << " >> " << _c << endl;}protected:int _a = 1;int _b = 1;int _c = 1;
};int main()
{A a;a.Print();return 0;
}

 输出:

我们发现上述就输出的是我们 缺省值。注意:此处不是初始化,是在声明的时候给的 缺省值。

 这里的 成员的 缺省值 是给编译器默认的 构造函数用的。

 因为是编译器默认使用 构造函数生成的缺省值,所以当我们在 类当中实现了 构造函数,这个缺省值就没有用了:

class A
{
public:A(int a, int b, int c){_a = a;_b = b;_c = c;}void Print(){cout << _a << " >>" << _b << " >> " << _c << endl;}protected:int _a = 1;int _b = 1;int _c = 1;
};int main()
{A a(9,9,9);a.Print();return 0;
}

 输出:

 

 此时我们Print()函数就输出的是, 9  >>  9  >>  9 ,不是 1  >>  1  >>  1  了。

 构造函数重载需要注意的点

 

class A
{
public:A(){_a = 2;_b = 2;_c = 2;}A(int a, int b, int c){_a = a;_b = b;_c = c;}void Print(){cout << _a << " >>" << _b << " >> " << _c << endl;}protected:int _a = 1;int _b = 1;int _c = 1;
};

 如上述的两个构造函数就构成了函数重载,构造函数的形参,同样是可以有缺省参数的:

	A(int a = 0, int b = 0, int c = 0){_a = a;_b = b;_c = c;}

 但是,构造函数中使用缺省参数会出现一些问题,如这个例子:

	A(){_a = 2;_b = 2;_c = 2;}A(int a = 0, int b = 0, int c = 0){_a = a;_b = b;_c = c;}

 比如这例子,在语法上是支持的,也就是说,上述两个构造函数是支持语法的,但是在调用无参数的构造函数的时候会出现一些问题:

 我们发现此时编译器就对该调用哪一个构造函数产生了歧义。

 当然我也不能  " A a1(); " 这样写,这样写与函数声明冲突,我们在下面会讲解。

 当然,我们给一个,或者两个参数都是可以的 ,因为他不会和 无参数的构造函数冲突。

所以,我们总结一下, 其实无参数构造函数和  全缺省 构造函数都属于是 默认构造函数,再加上 我们不写编译器就会生成的 默认构造函数,这三者只能有一个,因为这三者在外部的调用都是一样的编译器不能识别。

 

 构造函数的调用
 

 构造函数的调用跟普通函数也不太一样,普通函数代用是函数名 + 参数列表构造函数调用是对象名 + 参数列表。

 像上述的例子:


int main()
{A a2(9,9,9);a2.Print();return 0;
}

 在类当中定义的 构造函数 是 以 类名为函数名来创建的,但是调用的时候,使用的是 创建的对象的名字。

 当这个构造函数没有参数的时候,如下:

	A(){_a = 2;_b = 2;_c = 2;}

 那么,就直接创建这个对象,不需要加 () 参数列表,都会自己调用 类当中  不带参数的 构造函数,如果这个例子:
 

class A
{
public:A(){_a = 2;_b = 2;_c = 2;}A(int a, int b, int c){_a = a;_b = b;_c = c;}void Print(){cout << _a << " >>" << _b << " >> " << _c << endl;}protected:int _a = 1;int _b = 1;int _c = 1;
};int main()
{A a1;a1.Print();return 0;
}

 输出:

 我们发现此时输出的是,无参数 构造函数 当中初始化的值。

 注意:有参数是需要加 "()" 在 "()" 当中传参,而如果我们想调用 无参数的构造函数,那么我们是不能 加  "()"  的,如果加  "()" 就会报错:

 这是因为,如果这样写,会跟函数声明冲突,编译器不好识别。

 如上述的   " A a1(); "  是不是和 不带参数的 函数的 声明是一样,这样编译器就不好识别;但是如果是 带参数的 ,如:  " A a2(9,9,9); "  这样的就不会报错了,因为这样编译器就可识别了,因为我们在声明有参数函数的时候,参数是要加 参数的类型的: " void func ( int a, int b , int c);  " 这样的。而我们在调用有参数的构造函数的时候,是函数的传参,不需要 写 参数的类型。

 

 析构函数

 析构函数和构造函数的作用相反,它并不是完全的对  对象 进行销毁,局部对象进行销毁是由编译器来执行。所谓析构函数是对象在销毁的时候,才会执行的操作,来完成对象当中 一些成员的销毁工作。

特性

  •  析构函数的名字和类名相同,只是在最前面加上 "  ~  "   这个符号
  • 没有参数和返回值
  • 一个类只能有一个析构函数,若没有对析构函数进行定义,那么编译器会自动进行创建。注意:析构函数不能进行重载。
  • 在对象的生命周期结束的时候,编译器会自动调用析构函数。

析构函数的定义

如上篇当中的 栈的销毁:

class Stack
{~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
}

如上述的~Stack() 函数就是 析构函数,他在 栈的对象销毁的时候就会自动调用。

也就是说,在上篇中提到的,内存泄漏问题,在程序运行的最后,只需要销毁 这个对象就会自动的销毁创建的栈空间。

 有了析构函数和构造函数的时候就不在怕初始化和清理函数了,也简化了。

我们上述也提到了,析构函数不写编译器也会生成默认的 析构函数,那么这个 默认的析构函数和构造函数是一样的,也是内置成员不处理,自定义成员就要按照自定义类型进行处理。

 在比如上述栈的例子,如果类当中都是 自定义类型,那么我们也可以不写析构函数,编译器自动生成的 析构函数就会帮我们 清理销毁 对象空间。

 

 

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

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

相关文章

利用Python操作Mysql数据库

我们在进行Python编程的时候&#xff0c;时常要将一些数据保存起来&#xff0c;其中最方便的莫过于保存在文本文件了。但是如果保存的文件太大&#xff0c;用文本文件就不太现实了&#xff0c;毕竟打开都是个问题&#xff0c;这个时候我们需要用到数据库。提到数据库&#xff0…

json模块和pickle模块

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 json和pickle模块 json模块序列化与反序列化json模块中的方法 pickle模块 专栏&#xff1a;《python从…

JAVAweb开发学习

六、MybatisPlus快速上手 数据库操作 注意&#xff01;注意&#xff01;注意&#xff01;springboot版本选择2.7.2 1.ORM介绍&#xff08;对象关系映射&#xff09; 既包含存储&#xff0c;又包含映射。将java类映射到数据库 2.MybatisPlus介绍 ORM框架 数据库操作来啦…

【计算机网络】为什么 TCP 每次建立连接时,初始化序列号都要不一样呢?

【计算机网络】为什么 TCP 每次建立连接时&#xff0c;初始化序列号都要不一样呢&#xff1f; 为什么 TCP 每次建立连接时&#xff0c;初始化序列号都要不一样呢&#xff1f; 主要原因是为了防止历史报文被下一个相同四元组的连接接收。 TCP 四次挥手中的 TIME_WAIT 状态不是会…

机械键盘、口袋打印机,万元奖金等你拿!「万象格新」AI绘画X海报设计大赛即将开启...

号外&#xff01;「万象格新」大赛开启 如果阳光暖到你心里&#xff0c;那一定是一格在想你~ 春夏交替&#xff0c;万物焕发生机&#xff0c;明媚色彩娱情惬意 在这样一个美好的时节 如果你&#xff1a; 心中荡漾着色彩斑斓的 AI 绘画创意 想要 show 出独到的审美与非凡设计能力…

吴恩达团队AI诊断心律失常研究:准确率超人类医生

2019年&#xff0c;吴恩达团队在AI医疗领域实现了一项革命性的突破&#xff0c;他们成功地让AI诊断心律失常&#xff0c;其准确率高达83.7%&#xff0c;超过了人类心脏病医生的78.0%。这项研究成果已经发表在了知名期刊Nature Medicine上。 一、如何让AI学会诊断心律失常&…

闲谈【Stable-Diffusion WEBUI】的插件:美不美?交给AI打分

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;咖啡店艺术评价&#xff08;Cafe Aesthetic&#xff09; &#xff08;零&#xff09;前言 本篇主要提到了WEBUI的Cafe Aesthetic插件&#xff0c;这是一个相对独立的插件&#xff0c;单独标签页&#xff0c;…

Python小姿势 - Python基础知识

Python基础知识 Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。 Python的创始人为吉多范罗苏姆&#xff08;Guido van Rossum&#xff09;&#xff0c;于1989年底发布第一个公开发行版本——0.9.0。 自2004年以来&#xff0c;Python已经成为顶级开源项目&…

希尔排序的实现

希尔排序是插入排序的一种升级&#xff0c;其基本思想是&#xff1a; 先选定一个整数&#xff0c;把待排序文件中所有记录分成个组&#xff0c;所有距离为的记录分在同一组内&#xff0c;并对每 一组内的记录进行排序。然后&#xff0c;取&#xff0c;重复上述分组和排序的工 作…

使用Linux运维常识

一.基础操作 1.终端常用快捷键 快捷键描述ctrl键盘左键向左跳一个单词ctrl键盘右键向右跳一个单词Ctrl c停止当前正在运行的命令。Ctrl z将当前正在运行的命令放入后台并暂停它的进程。Ctrl d关闭当前终端会话。Ctrl l清屏&#xff0c;也可以用clear命令实现Tab自动补全当…

Asp.NET CORE实验室信息管理系统源码,支持IIS独立部署,Docker部署

技术架构&#xff1a;Asp.NET CORE 3.1 MVC SQLserver Redis等 基于B/S架构的实验室管理系统源码&#xff0c;整个系统的运行基于WEB层面&#xff0c;只需要在对应的工作台安装一个浏览器软件有外网即可访问。全套系统采用云部署模式&#xff0c;部署一套可支持多家医院检验…

自定义RecyclerView.LayoutManager实现类实现卡片层叠布局的列表效果

一.前言 先看效果&#xff08;大佬们请忽略水印&#xff09;&#xff1a; 卡片层叠列表的实现效果已经发布成插件&#xff0c;集成地址&#xff1a;implementation ‘com.github.MrFishC:YcrCardLayoutHepler:v1.1’&#xff1b; 先讲解如何快速实现&#xff0c;然后再来讲解…

托福高频真词List05 // 附托福TPO阅读真题

目录 4月23日单词 生词 熟词 4月24日真题 4月23日单词 生词 sparsethinly distributedadj 稀疏的sparselythinlyadv 稀疏地congestion / kənˈdʒestʃən / overcrowdingn 拥挤continuallyregularlyadv 持续的eradicateeliminatev 消除facilitatemake easiereasev 使..…

《面试1v1》java泛型

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a;小伙子,说实话,泛型这个机制一开始我也是一头雾水,搞不太明白它到底要解决什么问题。你能不能不那么书呆子,给我普普通通地讲一讲泛型? 候选人…

如何测试信号源或者发射机的回波损耗

信用源或者发射机的return loss测试过程 1.用网分线缆的第一步就是看线的抖动情况&#xff0c;后面还是要多注意 经过一系列排查后&#xff0c;选用两个抖动比较小的线缆&#xff0c;然后开始测试另外一台仪器。 2.检查测试仪器的输出功率&#xff0c;见图1 打开信号源或者发射…

可以一学的代码优化小技巧:减少if-else冗余

前言 if-else 语句对于程序员来说&#xff0c;是非常非常熟悉的一个判断语句&#xff0c;我们在日常开发和学习中都经常看见它&#xff0c;if-else语句主要用于需要做出选择的地方进行判断&#xff0c;这里就不再赘述if-else语法和特点了。 ​ 我们在写代码&#xff08;如图下…

PC1 - 搭建项目

先看路由&#xff0c;可以查看功能模块划分。熟悉什么看什么 router文件夹下routerConfig.tsx 配置路由&#xff0c;创建模块文件&#xff08;写好内容模块&#xff09;&#xff0c;lazy可懒加载导入。App.tsx配置一级路由&#xff0c;配置二级路由出口 { path:/, element: …

【记录】FFmpeg|超大视频本地有损压缩,500MB变5MB(支持 Windows、Linux、macOS)

参考&#xff1a; 如何将一分钟长的1080p视频压缩至5MB以内&#xff1f;-知乎-滔滔清风近期HEVC扩展备用安装方法-B站-悲剧天下 总共三个步骤&#xff0c;安装FFmpeg、运行指令、打开视频。 亲测 500MB 变 5MB。 1 安装FFmpeg 对于不需要看教程可以自行完成安装的同学们&…

7. 堆的简单学习

7. 堆 7.1 堆的定义 堆是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常可以被看做是一棵完全二叉树的数组实现。 堆的特性&#xff1a; 它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其它的每一层从左到右都是满的&#xff0c;如果最…

使用python实现自动点击功能

猜你感兴趣 使用Pyqt5玩转ChatGpt内网文件共享服务快速搭建私有pip镜像源python设计模式-创建型模式docker搭建私有git服务器&#xff0c;项目备份和迁移redis持久化方案 被测点击界面 新建counter.html添加下面代码并保存,使用编辑器或浏览器打开 <!DOCTYPE html> &l…