C++类与对象(拷贝与类的内存管理)

news2024/12/23 10:41:10

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步

个人主页:LaNzikinh-CSDN博客

文章目录

  • 前言
  • 一.对象的动态建立和释放
  • 二.多个对象的构造和析构
  • 三.深拷贝与浅拷贝
  • 四.C++类的内存管理
  • 总结

前言 

我们前面讲起了一些关于C++中类与对象的一些语法,构造函数C构函数初始化成员列表等等,也讲了面对对象的程序设计方法和面对过程的程序设计方法有什么区别,我们这次就主要针对类与对象的拷贝和一些存储内存的角度继续了解


一.对象的动态建立和释放

我们在C语言中动态开辟内存和释放内存,用用到的就是malloc函数和free函数,当然在C++中也是可用的,但是来到了C++,我们就要用C++的语法,在这个地方我们主要是用new和delete来动态建立和释放。

new和delete都是运算符,不是库函数,不需要单独添加头文件,而我们malloc和free都需要头文件,而且是函数,有函数的调用就要开辟栈空间,所以而运算符是不需要的,所以说这也体现的C++的好处

格式:
new
1、类型指针 指针变量名 = new 类型
2、类型指针 指针变量名 = new 类型(初始值)
3、类型指针 指针变量名 = new 类型[元素个数]
delete
1、delete 指针变量名
2、delete[] 指针变量名

int main()
{
	//在堆上申请一个int类型大小的空间,并且将申请的空间初始化为10
	int* p1 = new int(10);
	delete p1;
	//在堆上申请4个int类型大小的空间,并没初始化
	int* p2 = new int[4];
	delete[4] p2;
	//在堆上申请一个Box类型大小的空间,会构造对象出来
	Box* p3 = new Box;
	delete p3;
	//在堆上申请4个Box类型大小的空间,会构造对象出来
	Box* p4 = new Box[4];
	delete[4] p4;
	return 0;
}

注意

new和delete是运算符,不是函数,因此执行效率高。虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。new/delete 和 malloc/free有何取别呢?

1、malloc/free为C的标准库函数,new、delete则为C++的操作运算符
2、new能自动计算需要分配的内存空间,而malloc需要手工计算字节数
3、new与delete直接带具体类型的指针,malloc和free返回void类型的指针。
4、new类型是安全的,而malloc不是。例如int*p = new float[2];就会报错;
而int p = malloc(2sizeof(int))编译时编译器就无法指出错误来。
5、new调用构造函数,malloc不能;delete调用析构函数,而free不能
6.new/delete是操作符可以重载,malloc/free则不能

二.多个对象的构造和析构

我们之前学了析构函数和构造函数,但是有没有想过在多个对象中析构和构造的调用顺序是怎么样的呢?

注意:1.当类中的成员变量为另一个类的实例化对象时,我们称这个对象为成员对象。2.成员变量虽属的类中没有实现无参的构造函数是需要使用初始化成员列表。

#include<iostream>
using namespace std;
class ABC
{
public:
	ABC(int A, int B, int C)
	{
		cout << "ABC(int A, int B, int C)" << endl;
	}
	~ABC()
	{
		cout << "~ABC()" << endl;
	}
private:
	int a;
	int b;
	int c;
};
class myD
{
public:
	myD() :abc1(1, 2, 3), abc2(3, 5, 7)
	{
		cout << "myD()" << endl;
	}
	~myD()
	{
		cout << "~myD()" << endl;
	}


private:
	ABC abc1;
	ABC abc2;
};
int main()
{
	myD a;

	return 0;
}

调用顺序

最开始先是构造成员对象,所以先调用成员对象所对应的构造函数,然后就是构造函数本身,最后是析构函数,析构函数的调用顺序与构造相反,总之就是先构造成员对象,在构造本身,析构相反

三.深拷贝与浅拷贝

3.1拷贝构造函数

当使用已经构造好的对象t1,初始化一个新的对象就会调用拷贝构造函数

//拷贝构造函数
Test(const Test& t)
{
	cout << "Test(const Test& t)" << endl;
}

3.2对象的赋值

思考这样的赋值对吗?

#include<iostream>
using namespace std;
class Test
{
public:
	int x;
	int y;
	int* sum;
	Test(int a, int b):x(a),y(b)
	{
		sum = new int[4];
	}
	//拷贝构造函数
	Test(const Test& t)
	{
		cout << "Test(const Test& t)" << endl;
		x = t.x;
		y = t.y;
		sum = t.sum;
	}
	~Test()
	{
		delete[4] sum;
	}
};
int main()
{
	Test t1(10,20);
	t1.sum[0] = 10; t1.sum[1] = 11; t1.sum[2] = 12; t1.sum[3] = 13;
	Test t2 = t1;
}

答案是不对的,不可以直接这样赋值会出现问题,为什么呢?因为他们的sum的地址都指向同一个地方。这个赋值并没有开辟两个空间,而是让这两个成员变量都指向了同一个区域。调用析构函数的时候会释放两次,因此就会造成问题

这个就是拷贝错误,拷贝分为浅拷贝和深拷贝,同一类对象之间的负值一般是没有副作用的,但是类中有指针,并且指针指向的动态分配的内存空间时会导致两个对象的指针指向同一块内存空间,遇到这种情况时浅拷贝,他就不能解决问题,我们就要用深拷贝去解决

class Test
{
public:
	int x;
	int y;
	int* sum;
	Test(int a, int b):x(a),y(b)
	{
		sum = new int[4];
	}
	//拷贝构造函数
	Test(const Test& t)
	{
		cout << "Test(const Test& t)" << endl;
		x = t.x;
		y = t.y;
		//浅拷贝
	    //sum = t.sum;
		//深拷贝
		sum = new int[4];
	for (int i = 0; i < 4; i++)
	{
		sum[i] = t.sum[i];
	}
	}
	~Test()
	{
		delete[4] sum;
	}
};

3.3浅拷贝

1、同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝
2、一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的内存空间,将导致两个对象的指针变量向同一块内存空间,当两个对象被销毁时调用析构函数,因为在析构函数中会释放指针所指向的堆空间,造成同一块堆空间被释放两次从而导致程序运行出错。
3、如果我们没有实现拷贝构造函数,C++编译器会自动实现一个拷贝构造函数,我们称之为默认拷贝构造函数,但是在默认拷贝构造函数中实现的时浅拷贝

3.4深拷贝

实现拷贝构造函数,在拷贝构造函数中需要对对象中的指针变量进行单独的内存申请。两个对象中的指针变量不会指向同一块内存空间,然后再将右值对象指针所指向的空间中的内容拷贝到新的对象指针所指向的堆空间中。

四.C++类的内存管理

C++类和对象中成员变量和成员函数是分开存储的,成员变量:静态成员变量存储于全局数据区中普通成员变量存储于函数中与结构体变量有相同的字节对其方式。成员函数:存放于代码段

证明:

#include<iostream>
using namespace std;
class C1
{
public:
	int i;
	int j;
	int k;
};
class C2
{
public:
	int i;
	int j;
	int k;
	int getK()
	{
		return k;
	}
	void setK(int val)
	{
		k = val;
	}
};

int main()
{
	C1 c1;
	C2 c2;
	cout << sizeof(c1) << endl;
	cout << sizeof(c2) << endl;
}

4.2this指针

this指针的本质--指针常量,当形参和成员变量同名时,可用this指针来区分

using namespace std;
class ABC
{
public:
	int x, y, z;
	ABC(int x, int y, int z)
	{
		x = x;
		y = y;
		z = z;
	}
};
int main()
{
	ABC a(1, 2, 3);
	return 0;
}

经过编译

this指针指向调用该成员函数的对象

class ABC
{
public:
	int x, y, z;
	ABC(ABC*const this,int x, int y, int z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}
};
int main()
{
	//&a就是this指针
	ABC a(&a,1, 2, 3);
	return 0;
}

4.3类的静态成员变量

如果我要记录一个农场里面羊的数量,我该如何写呢?如果用C语言来写的话,就是面对过程的编程只有有样出生我就++,有羊死去我就减减,但是麻烦的是每个羊他可能会有年龄名字就会非常的繁琐,但是如果你是用c++面对对象的编程的话,我就可以直接构造一个样的类利用构造函数和析构函数来完成这个事情,而静态成员变量可以让这个事情完成的更完美,他是什么意思呢?可以用关键字static用于声明一个类的成员,静态的成员提供了一个同类对象的共享机制

#include<iostream>
using namespace std;
class sheep
{
public:
	int age;
	char name[32];
	sheep()
	{
		cnt++;
	}
	~sheep()
	{
		cnt--;
	}
	static int cnt;
};

int sheep::cnt = 0;
int main()
{

	return 0;
}

static int cnt;只是声明了一个静态成员变量,不是内或者对象的成员变量,但是他的作用与在内和这些类的所有实例化对象中

int sheep::cnt = 0;定义了sheep这个类中的静态成员变量cnt,并初始化为零,如果不初始化默认为4

4.4类的静态成员函数

定义:使用static修饰的成员函数叫做静态成员函数

在静态成员函数内,不能访问除静态成员函数以外的其他成员变量

什么时候可以将函数设计成静态成员函数?

函数的行为跟类的实例无关,只跟类有关

静态成员函数的用处:

1.访问被private/protected修饰静态成员变量
2.可以实现某些特殊的设计模式:如Singleton(单例模式)
3.可以封装某些算法,比如数学函数,如In,sin,tan等等,这些函数本就没必要属于任何一个对象,所以从类上调用感觉更好,比如定义一个数学函数类Math。


总结

这次我们主要讲解了对象的动态开辟和释放对比C语言的不同,和前面所讲到的析构和构造的一个升华,是多对象的析构和构造,还讲了C++独特的浅拷贝和深拷贝以及C++类的一些内存管理如类的静态成员变量静态成员函数和this指针

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

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

相关文章

数据仓库核心:事实表深度解析与设计指南

文章目录 1. 引言1.1基本概念1.2 事实表定义 2. 设计原则2.1 原则一&#xff1a;全面覆盖业务相关事实2.2 原则二&#xff1a;精选与业务过程紧密相关的事实2.3 原则三&#xff1a;拆分不可加事实为可加度量2.4 原则四&#xff1a;明确声明事实表的粒度2.5 原则五&#xff1a;避…

如何有效防御.360勒索病毒:.360勒索病毒加密文件预防方法探讨

导言&#xff1a; 随着信息技术的飞速发展&#xff0c;网络安全问题也日益凸显。其中&#xff0c;勒索病毒作为一种新型的网络安全威胁&#xff0c;给用户和企业带来了极大的困扰和损失。特别是.360勒索病毒&#xff0c;以其独特的加密方式和恶劣的勒索手段&#xff0c;引起了…

AtCoder Beginner Contest 356 G. Freestyle(凸包+二分)

题目 思路来源 quality代码 题解 对n个泳姿点(ai,bi)建凸包&#xff0c;实际上是一个上凸壳&#xff0c; 对于询问(ci,di)来说&#xff0c;抽象画一下这个图&#xff0c;箭头方向表示询问向量 按x轴排增序&#xff0c;并且使得后面的y不小于前面的y&#xff0c;因为总可以多…

Docker高级篇之Docker-compose容器编排

文章目录 1. Docker-compse介绍2. Docker-compse下载3. Docker-compse核心概念4. Docker-compse使用案例 1. Docker-compse介绍 Docker-compose时Docker官方的一个开源的项目&#xff0c;负责对Docker容器集群的快速编排。Docker-compose可以管理多个Docker容器组成一个应用&a…

【单片机毕业设计9-基于stm32c8t6的酒窖监测系统】

【单片机毕业设计9-基于stm32c8t6的酒窖监测系统】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 &#x1f525;这里是小殷学长&#xff0c;单片机毕业设计篇9基于stm32的酒窖监测系统 &#x1f9ff;创作不易&#xff0c;拒绝白嫖可私 一、功能介绍 -------------------…

Docker高级篇之轻量化可视化工具Portainer

文章目录 1. 简介2. Portainer安装 1. 简介 Portianer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便管理Docker环境&#xff0c;包括单机环境和集成环境。 2. Portainer安装 官网&#xff1a;https://www.portainer.io 这里我们使用docker命令安装&…

8.让画面动起来

一、Unity Shader中的内置变量&#xff08;时间篇&#xff09; 动画效果往往都是把时间添加到一些变量的计算中&#xff0c;以便在时间变化的同时也可以随之变化。Unity shader提供了一系列关于时间的内置变量来允许我们方便地在Shader中访问运行时间&#xff0c;实现各种动画…

STM32 | 独立看门狗 | RTC(实时时钟)

01、独立看门狗概述 在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状…

Elasticsearch:Open Crawler 发布技术预览版

作者&#xff1a;来自 Elastic Navarone Feekery 多年来&#xff0c;Elastic 已经经历了几次 Crawler 迭代。最初是 Swiftype 的 Site Search&#xff0c;后来发展成为 App Search Crawler&#xff0c;最近又发展成为 Elastic Crawler。这些 Crawler 功能丰富&#xff0c;允许以…

基于Java+SpringBoot制作一个景区导览小程序

基于Java+SpringBoot制作一个景区导览小程序。其中系统前端功能包括注册登录、景区采风、旅游导览、地图导航、发布采风、门票预订、修改个人信息;系统后台功能包括用户管理、景区管理、采风管理等模块。 摘要一、小程序1. 创建小程序2. 首页3. 景区采风页4. 旅游导览页5. 发布…

人工智能_机器学习097_PCA数据降维算法_数据去中心化_特征值_特征向量_EVD特征值分解原来和代码实现---人工智能工作笔记0222

降维算法的原理,一会咱们再看,现在先看一下,算法 可以看到PCA算法的,原理和过程,我们先看一下代码 为了说明PCA原理,这里,我们,先来计算一下X的方差,可以看到 先把数据进行去中心化,也就是用数据,减去数据的平均值. B = X-X.mean(axis=0) 这段代码是用于计算矩阵X的每一列减去该…

【Web世界探险家】3. CSS美学(二)文本样式

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

⌈ 传知代码 ⌋ 基于曲率的图重新布线

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

SQL159 每个创作者每月的涨粉率及截止当前的总粉丝量

描述 用户-视频互动表tb_user_video_log iduidvideo_idstart_timeend_timeif_followif_likeif_retweetcomment_id110120012021-09-01 10:00:002021-09-01 10:00:20011NULL210520022021-09-10 11:00:002021-09-10 11:00:30101NULL310120012021-10-01 10:00:002021-10-01 10:00…

如何获取MySQL中表的大小?(官方校正版)

与大多数关系数据库一样&#xff0c;MySQL 提供了有关数据库本身的有用元数据。虽然大多数其他数据库将此信息称为 catalog&#xff0c; 但MySQL 官方文档INFORMATION_SCHEMA 将元数据 称为 tables。 目录 1 列出单个数据库中的单表大小 2 列出所有数据库中的所有表大小 以下…

从年金理论到杠杆效应,再到财务报表与投资评估指标

一、解释普通年金终值和普通年金现值的概念。 普通年金终值&#xff1a;以利率为1%&#xff0c;每期收款100元&#xff0c;5期为例&#xff0c;普通年金终值的折算过程如图&#xff1a; 普通年金现值&#xff1a;以利率为1%&#xff0c;每期收款100元&#xff0c;5期为例&am…

【C++修行之道】类和对象(六)再谈构造函数(初始化列表)| explicit关键字 | static成员 | 友元|匿名对象|拷贝时一些编译器优化

目录 一、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1. 所有的成员,既可以在初始化列表初始化,也可以在函数体内初始化 2. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 3. 类中包含以下成员&#xff0c;必须放在初始化列表位置进行初始化&…

【设计模式】行为型设计模式之 备忘录模式(快照模式)

介绍 备忘录应用场景明确并且有限&#xff0c;一般用来数据的防丢失、撤销和恢复。对大对象的备份和恢复&#xff0c;备忘录模式能有效的节省时间和空间开销。 定义 备忘录模式&#xff1a;也称为快照模式&#xff0c;在不违背封装原则的前提下&#xff0c;捕获一个对象的内…

20240610 基于QGIS生成地区示意图的地图shp文件

目录 本文目标前置条件具体步骤1. 创建Project2. 插入世界地图3. 对地区示意图进行地理匹配4. 创建shp文件&#xff0c;勾画轨迹 注意事项 本文目标 基于QGIS生成地区示意图的地图shp文件&#xff0c;此shp文件可以用来学习&#xff0c;但是未经审批不可用于发表。 前置条件 …

水滴型锤片粉碎机:多功能粉碎利器

在现代工业生产中&#xff0c;粉碎机作为一种重要的机械设备&#xff0c;广泛应用于饲料、化工、木材等多个领域。其中&#xff0c;水滴型锤片粉碎机凭借其设计和粉碎能力&#xff0c;成为市场上的热门产品。 水滴型锤片粉碎机其设计灵感来源于水滴的形态。这种设计使得机器在…