类与对象C++详解(中)-----构造函数与析构函数

1.构造函数

        构造函数是一个特殊的成员函数,函数名和类名相同,构造函数的作用是初始化,以下是构造函数的一些特点:

1. 函数名与类名相同。

2. ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)

3. 对象实例化时系统会⾃动调⽤对应的构造函数。

4. 构造函数可以重载。

5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显 式定义编译器将不再⽣成。

6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函 数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫 默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调 ⽤的构造就叫默认构造。

7. 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始 化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤ 初始化列表才能解决,初始化列表,我们下个章节再细细讲解

说明:C++把类型分成内置类型(基本类型)和⾃定义类型。内置类型就是语⾔提供的原⽣数据类型, 如:int/char/double/指针等,⾃定义类型就是我们使⽤class/struct等关键字⾃⼰定义的类型。

好,那有没有人有疑问,什么叫做默认构造函数呢?没有参数或所有参数都有默认值(全缺省)的情况下就是默认构造函数,我们自己写的也算。

这里简单举两个例子,例1:

class A {
public:A(int a) {_a = a;}
private:int _a;
};
int main()
{A(5);return 0;
}

这里面的A(int a)对应的就是一个构造函数,但他不是默认的,接下来写一个默认的:

class A {
public:A(int a=5) {_a = a;}
private:int _a;
};
int main()
{A(5);return 0;
}

这个就是默认构造函数,因为他是全缺省的,接着:

#include<iostream>
using namespace std;
class A {
public:A(int a=5) {_a = a;}int _a;
};
class B {
public:A a;int b;
};
int main()
{A(5);B b;cout << b.a._a;return 0;
}

这个代码就可以正常运行,因为b在初始化时会默认调用A的默认构造函数,运行结果如下:

那接着我们来验证一下,如果不给A默认构造函数的话呢?

此时A没有默认构造函数,所以报错了,那我们将A的那个构造函数注释掉呢?

他又可以正确运行了,这就告诉我们,当我们显式的实现了构造函数之后,他将会删除掉之前的默认构造函数

注:这里我为了能够答应出来_a的值专门在声明的时候给了个初值,方便打印

2.析构函数

        析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,⽐如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对象中资源的清理释放⼯作。

        特点:

1. 析构函数名是在类名前加上字符~。

2. ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)

3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。

4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。

5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。

6. 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数。

7. 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;如 果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue;但是有资源申请时,⼀定要 ⾃⼰写析构,否则会造成资源泄漏,如Stack。

8. ⼀个局部域的多个对象,C++规定后定义的先析构。

举个例子:

#include<iostream>
using namespace std;
class A {
public:int* a=new int;~A(){delete a;a = nullptr;}
};int main()
{return 0;
}

一般只有动态开辟了空间,我们才需要显式的写析构函数。

3.拷贝构造函数

        如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外的参数都有默认值,则此构造函数 也叫做拷⻉构造函数,也就是说拷⻉构造是⼀个特殊的构造函数。

        特点:

1. 拷⻉构造函数是构造函数的⼀个重载。

2. 拷⻉构造函数的第⼀个参数必须是类类型对象的引⽤,使⽤传值⽅式编译器直接报错,因为语法逻 辑上会引发⽆穷递归调⽤。拷⻉构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引 ⽤,后⾯的参数必须有缺省值。

3. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返 回都会调⽤拷⻉构造完成。

4. 若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成 员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。

5. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的拷⻉构造就可以完 成需要的拷⻉,所以不需要我们显⽰实现拷⻉构造。像Stack这样的类,虽然也都是内置类型,但 是_a指向了资源,编译器⾃动⽣成的拷⻉构造完成的值拷⻉/浅拷⻉不符合我们的需求,所以需要 我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。像MyQueue这样的类型内部主要是⾃定义类型 Stack成员,编译器⾃动⽣成的拷⻉构造会调⽤Stack的拷⻉构造,也不需要我们显⽰实现 MyQueue的拷⻉构造。这⾥还有⼀个⼩技巧,如果⼀个类显⽰实现了析构并释放资源,那么他就 需要显⽰写拷⻉构造,否则就不需要。

6. 传值返回会产⽣⼀个临时对象调⽤拷⻉构造,传值引⽤返回,返回的是返回对象的别名(引⽤),没 有产⽣拷⻉。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤ 引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少 拷⻉,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。

 首先,什么是拷贝,上面的字太多了,肯定有人不想看,我就细细的给大家讲一下。

	A(A& a) {this->_a = a._a;cout << "A的拷贝构造函数\n";}

这就是拷贝构造,注意,这里一定要传引用,不知道引用是什么的同学可以去找其他文章看看。

假如我们不传引用会发生什么呢?这里会报错

为什么呢?首先,在调用拷贝构造函数时会出现形参,形参的值要从实参那里拷贝,然后拷贝的时候就会形成新的拷贝构造函数,就会形成一个死循环。

每次调用拷贝构造函数之前都要先传值传参,传值传参是一种拷贝,又形成一种新的拷贝构造,就形成了无穷递归

两种拷贝构造的调用方式:

接着我们把我们写的拷贝构造函数注释掉来看一下

为什么呢?原因在于c++他自己也提供了一个默认的拷贝构造函数,他能够完成大部分的工作,就是让里面的成员变量相等(内置类型),对于自定义类型,会调用这个自定义类型自己的拷贝构造函数,这种叫浅拷贝,既然都叫浅拷贝了,她一定会有自己的缺陷,没错那就是指针,我们接着往下看一段代码:

#include<iostream>
using namespace std;
class A {
public:int*_a;A(){_a = (int*)malloc(4);}~A(){free(_a);_a = NULL;}
};int main()
{A a1;A a2 = a1;return 0;
}

运行结果,why?

这就是编译器自己提供的拷贝构造函数的缺陷,再拷贝时她会这样:

但我们实际上的情况是想把地址内的值拷贝给他,而不是地址,那么在析构时呢?没错,这部分会被析构两次,会被free两次,会产生一些不可预测的后果,所以会报错,这时,就需要我们手动的去实现叫做深拷贝的一个东西。

        这里是一个示例代码,实际过程中我们可能需要加几个成员例如capacity,size等,确定所开空间大小。


#include<iostream>
using namespace std;
class A {
public:int*_a;A(){_a = (int*)malloc(4);}A(A& a){_a = (int*)malloc(sizeof(*a._a));*_a = *(a._a);}~A(){free(_a);_a = NULL;}
};int main()
{A a1;A a2 = a1;return 0;
}

什么时候需要我们实现显示的拷贝构造呢?首先就是动态内存分配过的,还有一个可以参考的地方就是,如果你显式的实现了析构函数,就肯定需要显式的实现拷贝构造函数

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

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

相关文章

计算机网络(1)基础篇

目录 1.TCP/IP 网络模型 2.键入网址--->网页显示 2.1 生成HTTP数据包 2.2 DNS服务器进行域名与IP转换 2.3 建立TCP连接 2.4 生成IP头部和MAC头部 2.5 网卡、交换机、路由器 3 Linux系统收发网络包 1.TCP/IP 网络模型 首先&#xff0c;为什么要有 TCP/IP 网络模型&a…

【JavaEE进阶】验证码案例

目 &#x1f332;实现说明 &#x1f384;Hutool介绍 &#x1f333;准备工作 &#x1f334;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f6a9;实现服务器后端代码 &#x1f6a9;前端代码 &#x1f6a9;整体测试 &#x1f332;实现说明 随着安全性的要求越来越⾼…

硬件学习笔记--42 电磁兼容试验-6 传导差模电流干扰试验介绍

目录 电磁兼容试验-传导差模电流试验 1.试验目的 2.试验方法 3.判定依据及意义 电磁兼容试验-传导差模电流干扰试验 驻留时间是在规定频率下影响量施加的持续时间。被试设备&#xff08;EUT&#xff09;在经受扫频频带的电磁影响量或电磁干扰的情况下&#xff0c;在每个步进…

机器学习·最近邻方法(k-NN)

前言 上一篇简单介绍了决策树&#xff0c;而本篇讲解与决策树相近的 最近邻方法k-NN。 机器学习决策树-CSDN博客 一、算法原理对比 特性决策树最近邻方法&#xff08;k-NN&#xff09;核心思想通过特征分割构建树结构&#xff0c;递归划分数据基于距离度量&#xff0c;用最近…

Deesek:新一代数据处理与分析框架实战指南

Deesek&#xff1a;新一代数据处理与分析框架实战指南 引言 在大数据时代&#xff0c;高效处理和分析海量数据是企业和开发者面临的核心挑战。传统工具如Pandas、Spark等虽功能强大&#xff0c;但在实时性、易用性或性能上仍有提升空间。Deesek&#xff08;假设名称&#xff…

【Vue】打包vue3+vite项目发布到github page的完整过程

文章目录 第一步&#xff1a;打包第二步&#xff1a;github仓库设置第三步&#xff1a;安装插件gh-pages第四步&#xff1a;两个配置第五步&#xff1a;上传github其他问题1. 路由2.待补充 参考文章&#xff1a; 环境&#xff1a; vue3vite windows11&#xff08;使用终端即可&…

JVM内存模型详解

文章目录 1. 程序计数器&#xff08;Program Counter Register&#xff09;2. Java虚拟机栈&#xff08;Java Virtual Machine Stacks&#xff09;3. 本地方法栈&#xff08;Native Method Stacks&#xff09;4. Java堆&#xff08;Java Heap&#xff09;5. 方法区&#xff08;…

KubeSphere 和 K8s 高可用集群离线部署全攻略

本文首发&#xff1a;运维有术&#xff0c;作者术哥。 今天&#xff0c;我们将一起探索如何在离线环境中部署 K8s v1.30.6 和 KubeSphere v4.1.2 高可用集群。对于离线环境的镜像仓库管理&#xff0c;官方推荐使用 Harbor 作为镜像仓库管理工具&#xff0c;它为企业级用户提供…

代码随想录-训练营-day30

今天我们要进入动态规划的背包问题&#xff0c;背包问题也是一类经典问题了。总的来说可以分为&#xff1a; 今天让我们先来复习0-1背包的题目&#xff0c;这也是所有背包问题的基础。所谓的0-1背包问题一般来说就是给一个背包带有最大容量&#xff0c;然后给一个物体对应的需要…

百问网(100ask)提供的烧写工具的原理和详解;将自己编译生成的u-boot镜像文件烧写到eMMC中

百问网(100ask)提供的烧写工具的原理 具体的实现原理见链接 http://wiki.100ask.org/100ask_imx6ull_tool 为了防止上面这个链接失效&#xff0c;我还对上面这个链接指向的页面保存成了mhtml文件&#xff0c;这个mhtml文件的百度网盘下载链接&#xff1a; https://pan.baidu.c…

【旋转框目标检测】基于YOLO11/v8深度学习的遥感视角船只智能检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

侯捷 C++ 课程学习笔记:C++ 面向对象开发的艺术

在侯捷老师的 C 系列课程中&#xff0c;《C 面向对象开发》这门课程让我对面向对象编程有了更深入的理解。面向对象编程&#xff08;OOP&#xff09;是现代软件开发中最重要的编程范式之一&#xff0c;而 C 作为支持 OOP 的语言&#xff0c;提供了强大的工具和特性。侯捷老师通…

神经网络常见激活函数 12-Swish函数

Swish 函数导函数 Swish函数 S w i s h ( x ) x ⋅ σ ( β x ) x 1 e − β x \begin{aligned} \rm Swish(x) & x \cdot \sigma(\beta x) \\ & \frac{x}{1 e^{-\beta x}} \end{aligned} Swish(x)​x⋅σ(βx)1e−βxx​​ Swish函数导数 d d x S w i s h ( x…

CF 137B.Permutation(Java 实现)

题目分析 输入n个样本&#xff0c;将样本调整为从1到n的包含&#xff0c;需要多少此更改 思路分析 由于样本量本身就是n&#xff0c;无论怎么给数据要么是重复要么不在1到n的范围&#xff0c;只需要遍历1到n判断数据组中有没有i值即可。 代码 import java.util.*;public clas…

web第三次作业

弹窗案例 1.首页代码 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>综合案例</title><st…

go语言简单快速的按顺序遍历kv结构(map)

文章目录 需求描述用map实现按照map的key排序用二维切片实现用结构体实现 需求描述 在go语言中&#xff0c;如果需要对map遍历&#xff0c;每次输出的顺序是不固定的&#xff0c;可以考虑存储为二维切片或结构体。 假设现在需要在页面的下拉菜单中展示一些基础的选项&#xff…

Unity 命令行设置运行在指定的显卡上

设置运行在指定的显卡上 -force-device-index

分享一个使用的音频裁剪chrome扩展-Ringtone Maker

一、插件简介 铃声制作器是一个简单易用的 Chrome 扩展&#xff0c;专门用于制作手机铃声。它支持裁剪音频文件的特定片段&#xff0c;并将其下载为 WAV 格式&#xff0c;方便我们在手机上使用。无论是想从一段长音频中截取精彩部分作为铃声&#xff0c;还是对现有的音频进行个…

数据开放共享和平台整合优化取得实质性突破的智慧物流开源了

智慧物流视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本可通过边缘计算技术…

预留:大数据Hadoop之——部署hadoop+hive+Mysql环境(Linux)

传送门目录 前期准备 一、JDK的安装 1、安装jdk 2、配置Java环境变量 3、加载环境变量 4、进行校验 二、hadoop的集群搭建 1、hadoop的下载安装 2、配置文件设置 2.1. 配置 hadoop-env.sh 2.2. 配置 core-site.xml 2.3. 配置hdfs-site.xml 2.4. 配置 yarn-site.xm…