当前位置: 首页 > news >正文

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

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

个人主页: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类型大小的空间,并且将申请的空间初始化为10int* 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指针

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Ubuntu 22.04 下 CURL 分块上传文件(C++)
  • AIRNet模型使用与代码分析(All-In-One Image Restoration Network)
  • 【小白学Python】自定义图片的生成(二)
  • 用Python绘制yolo训练结果比较图-论文需要
  • 鸿蒙轻内核A核源码分析系列六 MMU协处理器(1)
  • 【稳定检索/投稿优惠】2024年智慧金融与财务管理国际会议(SFFM 2024)
  • C#操作MySQL从入门到精通(21)——删除数据
  • C# 设置PDF表单不可编辑、或提取PDF表单数据
  • 开发TEE的踩坑之配置PCCS
  • 子域名爆破工具
  • 拜托:不要像鲍勃大叔那样重构
  • eNSP学习——RIP路由协议的汇总
  • DP:回文串模型
  • 【数据结构】利用单链表再实现通讯录
  • 编程的作品怎么删除编程库:一步步的深入解析
  • CODING 缺陷管理功能正式开始公测
  • DataBase in Android
  • Effective Java 笔记(一)
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • PHP 7 修改了什么呢 -- 2
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Python中eval与exec的使用及区别
  • React系列之 Redux 架构模式
  • Redis字符串类型内部编码剖析
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 回顾 Swift 多平台移植进度 #2
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 如何利用MongoDB打造TOP榜小程序
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #{} 和 ${}区别
  • #FPGA(基础知识)
  • #Linux(make工具和makefile文件以及makefile语法)
  • #传输# #传输数据判断#
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (待修改)PyG安装步骤
  • (十六)串口UART
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (算法设计与分析)第一章算法概述-习题
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • ***原理与防范
  • *算法训练(leetcode)第三十九天 | 115. 不同的子序列、583. 两个字符串的删除操作、72. 编辑距离
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .net 无限分类
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .net开发时的诡异问题,button的onclick事件无效
  • @serverendpoint注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)