构造函数的复习,析构函数,拷贝构造函数与由此关于引用的思考

TIPS

  1. 在类当中不受访问限定符的限制,在类外面才会受到限制
  2. 由于内存栈区的使用习惯是先使用高地址,再使用低地址;因此比方说有两个实例化对象依次创建,并且这两个实例化对象当中都有析构函数,也就是当退出销毁的时候,都承担着归还内存堆区空间给操作系统的任务,那么应该是后创建的实例化对象先执行释放堆区空间的任务,因为栈区的使用习惯是地址从高到低,那么当退出销毁归还给操作系统的时候就是按从低到高的地址顺序
  3. 实际上对于类的实例化对象当中的自定义类型成员,无论是从自己去实现构造函数,析构函数与拷贝构造函数,还是编译器去默认生成的角度去看的话,那些自定义类型成员都是比较省心的,因为自定义类型成员变量它本质上也是一个五脏俱全的类的实例化对象,我只需要去调用它的构造函数/析构函数/拷贝构造函数即可。

构造函数复习与回顾

在这里插入图片描述

  1. 构造函数支持函数重载
  2. 默认构造函数就是无参的构造函数全缺省的构造函数,或者说编译器自己生成的构造函数,这三个函数被称为默认构造函数,默认构造函数的话有且只能存在一个
  3. 如果说用户没有去显示的去定义一个构造函数,那么编译器会默认生成一个无参的构造函数,注意编译器自己生成的一个默认构造函数是没有参数的,并且对于编译器自己生成的默认构造函数,对于某些属于内置类型的成员变量是不做处理的,但是对于那些自定义类型的成员变量会进行处理。
  4. 并且构造函数一定要放在类当中的public区域的,因为如果你把构造函数放在私有区域的话,当类的实例化对象创建的时候,会一语双关去调用这个构造函数,但如果这个构造函数在私有区域的话,就无法调用与访问,所以说,就会报错。
    在这里插入图片描述
    在这里插入图片描述
  5. 构造函数在整个对象的生命周期内是只能够调用一次,然后构造函数的调用与类的实例化对象创建在语法上面格式一模一样,所以相当于是一语双关,然后构造函数的调用(就是类的实例化对象的创建)如果说没有参数的话,后面是不能加上括号的,这个与普通的函数调用并不一样
  6. 在c++11当中打了一个补丁,也就是说当在创建类的时候,可以对这些类的内置类型成员变量去给一个初始值(缺省值),那么这样子的话,编译器默认生成的构造函数将会用缺省初始值对于那些内置类型的成员变量进行初始化。

编译器自己生成的默认构造函数对于内置类型与自定义类型处理详解

在这里插入图片描述

  1. 在一般情况下,构造函数都需要我们自己去写,当然也有以下几种特殊情况:因为如果你不想去写构造函数的话,就要寄希望于编译器自己生成的构造函数能够完成对于你这个类的对象各个成员进行初始化的工作。但问题就在于编译器自己生成的构造函数对于内置类型不会进行初始化,所以说如果说你的内置类型都有缺省值的话且初始化符合我们的要求那么就可以用编译器默认生成的构造函数。
  2. 然后之前一直在提编译器生成的默认构造函数会对自定义类型的成员变量进行初始化:这边就需要注意两点,首先各种各样杂七杂八的指针,无论它指向的内存空间里面的数据类型是什么,但终究还是指针,并不属于自定义类型。第二就在于,对于正儿八经的自定义类型如class,struct…的成员变量,编译器默认生成的构造函数 确实会对他进行初始化,但结合之前的一语双关的知识,会发现这个初始化需要建立在这些类型都进行默认构造的前提之下。这边必须结合一语双关的那个知识
  3. 其实编译器默认生成的那个构造函数对于自定义类型的成员变量进行初始化,你可以把它理解成:首先你先创建了一个类的实例化对象,那么这个实例化对象里面它势必会包含那个自定义类型的成员变量,所以说你在创建类的实例化对象的时候,其实也是在创建了那个自定义类型的成员变量。那么既然那个自定义类型的成员变量被创建出来,如stack s1; 那么这时候就可以用之前讲的一语双关的看法去看待他,于此同时你会发现确实会进行初始化,只不过是那个自定义类型的成员变量在创建的时候调用的那个构造函数是无参的,也就是那三个默认构造函数当中的其中某一个。那如果说对于那个自定义类型的成员变量要带参进行初始化,那这个就涉及到之后的知识…
    在这里插入图片描述

析构函数

  1. 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

在这里插入图片描述
3. 析构函数如果我们不写的话,编译器也会自动默认生成一个,对于编译器自己生成的默认析构函数,对于这个类的对象当中的内置类型的成员变量的话不做任何处理,对于自定义类型的成员变量的话去对应的调用它的析构函数
4. 一般情况下,如果说向内存的堆区动态申请资源就需要显示写析构函数释放资源,不然就会造成内存泄露;然后如果没有动态申请的资源,就不需要写析构函数;或者说需要释放的成员都是自定义类型,那也就不需要去写析构函数,因为编译器自己生成的析构函数会对那些自定义类型的成员变量进行处理(实际上就是去调用那些自定义类型的析构函数)
在这里插入图片描述
简单示例代码

#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
	//构造函数
	A(int size)
	{
		_size = size;
		_pa = (int*)malloc(sizeof(int) * _size);
		if (_pa == NULL)
		{
			perror("malloc failed");
			return;
		}
	}
	void input()
	{
		for (int i = 0; i < _size; i++)
		{
			scanf("%d", &_pa[i]);
		}
	}
	void output()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << _pa[i] << " ";
		}
		cout << endl;
	}
	~A()
	{
		free(_pa);
		_size = 0;
	}
private:
	int* _pa;
	int _size;
};
int main()
{
	A a(10);
	a.input();
	a.output();
	return 0;
}

在这里插入图片描述

拷贝构造函数

  1. 如果说实例化对象进行函数传值传参(包括一语双关当中的构造函数)或者实例化对象之间进行赋值(还有一种场景就是函数的传值返回,因为传值返回的话,在函数的外面就会有一个东西去接受返回值,所以本质上就是赋值)这就意味着有新的实例化对象生成,也就意味着一语双关会调用构造函数,此时调用的这个构造函数就是拷贝构造函数。
  2. 由于他也是默认成员函数,所以说可以自己写一个在类当中,如果自己没有显示实现的话,编译器也会自己默认生成一个拷贝构造函数。
  3. 拷贝构造函数:只有单个形参***,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用***
  4. 对于编译器自己默认生成的拷贝构造函数的话,对于内置类型成员也会进行处理,对于自定义类型成员肯定会进行处理。对于内置类型成员完成值拷贝或浅拷贝(就是说将对象以内存存储按字节序完成拷贝,就是类似于像memcpy一样,一个字节一个字节的给他拷贝过去,是相当直白与耿直的拷贝);然后对于自定义类型的成员变量,就去调用他的拷贝构造函数。
  5. 按道理来说,编译器自己生成的默认拷贝构造函数已经能够满足拷贝实例化对象的一个需求,实际上大部分情况都是这样。但也会存在着特殊情况会发生严重的错误:比如说在实例化对象当中,有一个成员指针指向的是一块内存堆区上的空间,此时如果用编译器的默认拷贝构造函数,那么也会把那个成员指针按每个字节给他全部拷贝到新的实例化对象当中,就会导致两个实例化对象的成员指针指向的都是同一块内存堆区空间。在这里插入图片描述
  6. 这就会造成两个问题***:1. 首先就是在这两个实例化对象消亡的时候会调用两次析构函数,也就意味着同一块内存堆取空间被释放free了两次;2. 这两个实例化对象共同指向同一内存空间,就使得他们已经丧失了相互之间的独立性,只要在某一个实例化对象当中修改一下内存数据,对于另一个实例化对象也会受到影响。***
  7. 注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

在这里插入图片描述

关于拷贝构造函数参数不能传值而需要用引用的解释

  1. 因为对于一个实例化对象,如果说需要参与到函数的传值传参调用的话,那么必须要先进入到该类的拷贝构造函数当中,如果说拷贝构造函数的参数也是传值的话,那么这时候相当于又是类的实例化对象在进行函数的传值传参调用,那么又需要去进入到拷贝构造函数,如此一来的话,就会无穷递归下去
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
    在这里插入图片描述

关于自己写拷贝构造函数时的const修饰问题

  1. 这就涉及到之前的引用的时候,权限能够给平移或者缩小,但是不能够进行权限放大,所以说在引用某一个变量或者内存空间的时候,如果说在特定的情形之下,那个内存空间里面的数据是不能被修改的,那么某个外号的引用一般都需要用const的修饰一下去引用,因为权限是可以平移或缩小的。
  2. 由于我是依据一个已经存在的实例化对象,然后把它各个成员的数据拷贝到一个新的实例化对象,那么我原先的实例化对象当中,各个成员的数据肯定是不能被修改的,因此在引用的时候,必须要用const的修饰一下。

从拷贝构造函数得到关于引用的一些思考

  1. 所以这也从侧面角度更加凸显出了引用参与到函数传参与函数返回的重要性,尤其是当涉及到实例化对象进行函数传参与函数返回的时候,如果说不进行引用的优化的话,那么这时候会有新的实例化对象的创建与调用拷贝构造函数,尤其是当为深拷贝的时候,效率就会放慢,但是如果我用引用的话,效率就会高不少。
  2. 为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

代码模拟(我自己实现的拷贝构造函数(深拷贝))

#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
	//构造函数
	A(int size)
	{
		_capacity = size;
		_pa = (int*)malloc(sizeof(int) * _capacity);
		if (_pa == NULL)
		{
			perror("malloc failed");
			return;
		}
	}
	//拷贝构造函数
	A(const A& a)
	{
		_capacity = a._capacity;
		_pa = (int*)malloc(sizeof(int) * a._capacity);
		if (_pa == NULL)
		{
			perror("malloc failed");
			return;
		}
		memcpy(_pa, a._pa, sizeof(int) * a._capacity);
	}
	void input()
	{
		for (int i = 0; i < _capacity; i++)
		{
			scanf("%d", &_pa[i]);
		}
	}
	void output()
	{
		for (int i = 0; i < _capacity; i++)
		{
			cout << _pa[i] << " ";
		}
		cout << endl;
	}
	~A()
	{
		free(_pa);
		_capacity = 0;
	}
private:
	int* _pa;
	int _capacity;
};
int main()
{
	A a(10);
	cout << "往实例化对象a里面输入:" << endl;
	a.input();
	cout << "实例化对象a里面的数据" << endl;
	a.output();
	A b = a;
	cout << "实例化对象b是a的拷贝,b目前数据如下:" << endl;
	b.output();
	cout << "修改a的数据,重新输入" << endl;
	a.input();
	cout << "现在实例化对象a里面的数据" << endl;
	a.output();
	cout << "现在实例化对象b里面的数据" << endl;
	b.output();
	return 0;
}

在这里插入图片描述

代码模拟(编译器自己生成的拷贝构造函数(浅拷贝))

#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
	//构造函数
	A(int size)
	{
		_capacity = size;
		_pa = (int*)malloc(sizeof(int) * _capacity);
		if (_pa == NULL)
		{
			perror("malloc failed");
			return;
		}
	}
	void input()
	{
		for (int i = 0; i < _capacity; i++)
		{
			scanf("%d", &_pa[i]);
		}
	}
	void output()
	{
		for (int i = 0; i < _capacity; i++)
		{
			cout << _pa[i] << " ";
		}
		cout << endl;
	}
	~A()
	{
		free(_pa);
		_capacity = 0;
	}
private:
	int* _pa;
	int _capacity;
};
int main()
{
	A a(10);
	cout << "往实例化对象a里面输入:" << endl;
	a.input();
	cout << "实例化对象a里面的数据" << endl;
	a.output();
	A b = a;
	cout << "实例化对象b是a的拷贝,b目前数据如下:" << endl;
	b.output();
	cout << "修改a的数据,重新输入" << endl;
	a.input();
	cout << "现在实例化对象a里面的数据" << endl;
	a.output();
	cout << "现在实例化对象b里面的数据" << endl;
	b.output();
	return 0;
}

在这里插入图片描述
在这里插入图片描述
上面这个报错主要在于一块堆区空间被释放完了之后又去释放一遍

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

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

相关文章

CompletableFutrue异步处理

异步处理 一、线程的实现方式 1. 线程的实现方式 1.1 继承Thread class ThreadDemo01 extends Thread{Overridepublic void run() {System.out.println("当前线程:" Thread.currentThread().getName());} }1.2 实现Runnable接口 class ThreadDemo02 implements …

UDP协议介绍

文章目录 一、端口号二、UDP协议1.UDP协议格式2.UDP协议的特点3.UDP缓冲区 三、UDP注意事项 一、端口号 端口号是在网络中标识一台主机上进行通信程序的唯一性的&#xff0c;在TCP/IP协议中&#xff0c;用源IP、源端口号、目的IP、目的端口号、协议号这样一个五元组来标识一个…

[工具]Pytorch-lightning的使用

Pytorch-lightning的使用 Pytorch-lightning介绍Pytorch-lightning与Pytorch的区别Pytorch-lightning框架的优势Pytorch-lightning框架 重要资源 Pytorch-lightning介绍 这里介绍Pytorch_lighting框架. Pytorch-lightning与Pytorch的区别 Pytorch-lightning可以简单的看作是…

计算机图形学 | 实验六:旋转立方体

计算机图形学 | 实验六&#xff1a;旋转立方体 计算机图形学 | 实验六&#xff1a;旋转立方体Z-缓冲GLM函数库PVM矩阵PVM矩阵的使用 华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff09; 计算机图形学 | 实验六&#xff1a;旋转…

携创教育:自考、成考、开放大学几年能够毕业拿证?

目前&#xff0c;国家承认的成人学历提升的形式只有3种&#xff0c;分别是自考&#xff0c;成考&#xff0c;开放大学。 ▼各学历形式拿证时间▼ ★自学考试 自考没有入学考试&#xff0c;只需要参加相应的课程考试&#xff0c;所有课程考试合格后&#xff0c;符合毕业条件即可…

【Linux】usb游戏手柄测试、编程

1、简述 在ubuntu18.04下使用usb游戏手柄,之前联系客服,客服回答不清楚是否支持linux,因此采购一款北通蝙蝠2的手柄来测试 2、测试 2.1 测试环境 系统:Ubuntu18.04 正常电脑系统ubuntu中都是自带手柄驱动的joystick,即内核配置已添加选项:Joysticks interface和Joys…

制作帮助中心过程中常见的误区与解决方法?

制作帮助中心是为了帮助用户了解产品和解决问题的重要手段。然而&#xff0c;在制作的过程中&#xff0c;我们可能会遇到一些误区&#xff0c;这些误区可能会导致我们的帮助中心无法达到预期的效果。因此&#xff0c;在本文中&#xff0c;我们将探讨制作帮助中心过程中常见的误…

try(){}用法try-with-resources、try-catch-finally

属于Java7的新特性。 经常会用try-catch来捕获有可能抛出异常的代码。如果其中还涉及到资源的使用的话&#xff0c;最后在finally块中显示的释放掉有可能被占用的资源。 但是如果资源类已经实现了AutoCloseable这个接口的话&#xff0c;可以在try()括号中可以写操作资源的语句(…

Oracle SQL优化相关数据项

要掌握SQL调优技术,就需要能读懂SQL语句的执行计划,要想读懂SQL语句的执行计划,不仅需要准确理解SQL语句执行计划中各操作及其含义,还需要准确理解SQL语句执行计划中各数据项的含义。本书第7章中,已经对SQL语句执行计划中各个操作的含义做了详尽的阐述,本章中,我们将对S…

(4)Qt——基本组件

目录 1. Designer 设计师** 2. Layout 布局*** 3. 基本组件 3.1 QWidget** 3.2 ui指针 3.3 QLabel 标签** 3.4 QAbstractButton 按钮类** 3.5 QLineEdit 单行文本输入框** 3.6 QComboBox 组合框** 3.7 一组与数值相关的组件* 1. Designer 设计师** Designer是一款独立的用于设计…

ShardingSphere系列四(Sharding-JDBC内核原理及核心源码解析)

文章目录 1. ShardingSphere内核解析1.1 解析引擎1.2 路由引擎1.3 改写引擎1.4 执行引擎1.5 归并引擎 2. ShardingSphere的SPI扩展点2.1 SPI机制2.2 ShardingSphere中的SPI扩展点2.3 实现自定义主键生成策略 3. ShardingSphere源码 1. ShardingSphere内核解析 ShardingSphere虽…

SuperMap GIS基础产品桌面GIS FAQ集锦(2)

SuperMap GIS基础产品桌面GIS FAQ集锦&#xff08;2&#xff09; 【iDesktop】【10.2.1】【11.0.1】 请问在 iDesktop 桌面端对线数据集进行打断线操作后&#xff0c;打断的线不显示是什么原因呢&#xff1f; 【问题原因】 当时操作的线数据集空间索引存在异常&#xff0c;导致…

PostgreSQL 新闻速递 谷歌基于POSTGRESQL 兼容数据库提供更大规模的数据库服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

初级算法-贪心算法

主要记录算法和数据结构学习笔记&#xff0c;新的一年更上一层楼&#xff01; 初级算法-贪心算法 一、分发饼干二、摆动序列三、最大子序和四、买卖股票最佳时机五、跳跃游戏六、跳跃游戏二七、k次取反后最大化的数组和八、加油站九、分发糖果十、柠檬水找零十一、根据身高重建…

David Silver Lecture 4: Model-Free Prediction

1 Introduction 任务&#xff1a;第三章使用动态规划方法&#xff0c;解决known的MDP问题&#xff0c;这章通过model free prediction对一个unknown的MDP估计他的value function。下一章通过Model free control的方法针对一个unknown的MDP optimise value function。 2 Monte…

Android类似微信聊天页面教程(Kotlin)五——选择发送图片

前提条件 安装并配置好Android Studio Android Studio Electric Eel | 2022.1.1 Patch 2 Build #AI-221.6008.13.2211.9619390, built on February 17, 2023 Runtime version: 11.0.150-b2043.56-9505619 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Windows 11 …

paddleLite在Android部署初体验(环境问题)

paddleLite初体验&#xff08;环境问题&#xff09; Android Studio下载Paddle Lite Demo打开项目环境配置下载到手机 Paddle Lite是百度开发的一种方便部署的深度学习推理框架&#xff0c;笔者最近想接触一些模型部署相关项目&#xff0c;就先接触了一下Paddle Lite&#xff0…

eks实践案例

Eks&#xff1a; ami-0c23197c88296c1b5 eks集群&#xff1a; 拉面 - 知乎 https://blog.csdn.net/saynaihe/category_12204222.html 什么是 Amazon EKS&#xff1f; - Amazon EKS kubectl 使用指南 https://zhuanlan.zhihu.com/p/364994610 k8s HPA自动伸缩 手把手教你 K8…

【花雕学AI】我们如何才能避免被ChatGPT替代?——一个跨学科的视角

ChatGPT是一个由OpenAI开发的AI文本工具&#xff0c;它可以理解和生成自然语言&#xff0c;从而与用户进行对话。ChatGPT是基于GPT-3或者GPT-4模型的&#xff0c;这是目前最大和最先进的语言模型之一。ChatGPT通过在大量的互联网文本数据上进行预训练和强化学习&#xff0c;学习…

Mongodb 在工业场景下的数字解决方案

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…
最新文章