C++中的内存管理

news/2024/2/21 4:00:54

✨前言✨

📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2023年11月21日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!

C++内存管理

  • 1 C++内存分布
  • 2 C++内存管理方式
    • 2.1 处理内置类型
    • 2.2 处理自定义类型
  • 3 operator new与operator delete函数
  • 4 定位new表达式
  • 5 malloc/free和new/delete的区别

1 C++内存分布

在这里插入图片描述
C++根据不同的数据需求有着不同的存储特性,所以将内存分为多个区域来存储数据!

2 C++内存管理方式

在C语言中,我们是利用malloc/calloc/relloc进行动态内存管理!其中relloc就是对之前malloc申请的空间进行扩展,calloc就是malloc加上memset(),而C++是通过new和delete操作符进行动态内存管理

2.1 处理内置类型

利用new和delete操作符进行动态内存管理比C语言中的malloc方便好写了许多!

int main()
{int* b1 = new int;    //申请一个int的空间int* b2 = new int[5]; //利用new[]操作符,申请5个连续int的空间,就是一个数组delete b1;delete[] b2;return 0;
}

通过调试,我们可以进一步的了解new和delete操作符是如何处理内置类型的!
在这里插入图片描述
可以发现,new其实和C语言中的malloc是一样的,也是不会进行初始化的,只是动态开辟空间!相比较于malloc而言,new是不用进行强制类型转换,也不用自己去计算要开辟空间的大小了!同理,delete就相当于free,释放对应开辟的空间!

注意:new和delete要搭配使用,new[]和delete[]搭配使用!不要混着使用,否则会出现内存泄露或者程序不能正常运行的问题!

利用new其实也是可以对内置类型进行初始化的!代码如下:

int main()
{int* p1 = new int(10); //开辟一个int空间,结合()中的内容进行初始化int* p2 = new int[5] {1, 2, 3}; //开辟5个连续的int空间,结合{}中的内容进行初始化delete p1;delete[] p2;return 0;
}

调试结果如图所示:
在这里插入图片描述
可以发现对于5个连续的int空间,只要我们给定一些空间的初始化,后面的空间编译器就把它初始化成为0的!

2.2 处理自定义类型

对于自定义类型如果使用malloc,我们就无法对自定义类型进行初始化!因为构造函数不可以通过所给的对象指针直接调用:
在这里插入图片描述
利用new和delete操作符如何处理自定义类型呢?我们可以参考如下代码进行理解:

class Date {
private:int _year;int _month;int _day;public:Date(int year = 2023 , int month = 11, int day = 21){cout << "Date 构造函数调用了" << endl;_year = year;_month = month;_day = day;}~Date(){cout << "析构函数被调用了" << endl;}
};int main()
{Date* d1 = new Date(2023,11,21);Date* d2 = new Date[5]{ Date(2023,11,20) };//初始化一部分,其他的是默认构造初始化,如果没有默认构造也会报错Date* d3 = new Date;//调用默认构造,无默认构造就会报错delete d1;delete[] d2;delete d3;return 0;
}

运行结果如下:
在这里插入图片描述

从以上我们就可以知道new和delete处理自定义类型的原理

new就是开辟对象空间+调用构造函数,malloc就不会调用构造函数
delete就是调用析构函数+释放对象空间,free就不会调用析构函数

3 operator new与operator delete函数

operator new和operator delete是系统提供的全局函数!我们先来看这样一段代码:

class Stack {
private:int* p;int _capacity;int _top;public:Stack(int capacity = 4){cout << "Stack(int capacity = 4)" << endl;p = new int[capacity];_capacity = capacity;_top = 0;}~Stack(){cout << "~Stack()" << endl;delete[] p;_capacity = _top = 0;p = nullptr;}};
int main()
{Stack st1;Stack* st2 = new Stack;return 0;
}

如何理解上述的两个new呢?如图所示:
在这里插入图片描述
第一个new是开辟对象的空间,第二个new是开辟对象中p所指的空间!这也就和之前的知识点联系上了,利用delete就是先调用析构,清理对象中的额外资源也就是动态开辟所需的空间!在释放对象的空间!通过这个例子,我们可以更深刻的理解new和delete的工作原理!那么new和delete底层是怎样实现的呢?
运行以下这段代码:

int main()
{Stack* st1 = new Stack;Stack* st2 = (Stack*)operator new(sizeof(Stack));return 0;
}

调试与运行结果如图:
在这里插入图片描述

在这里插入图片描述
我们可以发现通过operator new函数和malloc一样,只是开辟了空间,并不会调用构造函数进行初始化!实际上operator new底层封装的就是malloc!那为什么不直接用malloc呢?那是因为我们要面向对象编程,malloc返回值是0,我们通过operator new是可以抛异常的!更加符合面向对象编程!而operator delete也同理,是封装了free!简单来说既可以用下图来概括!
在这里插入图片描述
在这里插入图片描述

4 定位new表达式

可以通过定位new显式的调用构造函数!在实际应用中,定位new一般是配合内存池使用的,因为内存池分配出来的空间没有初始化,因此如果需要在这块内存池分配出来的空间上构造自定义类型的对象,需要使用定位new显式调用构造函数构造目标对象

格式如下:
new(申请对象地址)类型
new(申请对象地址)类型(类型的初始化列表)

例子如下:

//定位new的使用class A {
private:int _a;int _b;public:A(int a = 10){cout <<"A(int a = 10)"<< endl;_a = a;}A(int a , int b ){cout << "A(int a = 1, int b = 2)" << endl;}~A(){cout << "~A()" << endl;}
};int main()
{A* a1 = (A*)operator new(sizeof(A));//开辟对象的空间new(a1)A;//定位new,显式的调用构造函数,调用默认构造a1->~A();//显式的调用析构函数free(a1);//释放对象空间A* a2 = (A*)operator new(sizeof(A));new(a2)A(10, 20);//定位new,调用两个参数的构造函数a2->~A();free(a2);
}

显式调用了构造函数,那就要显式调用析构函数,要配对使用就可以了!

5 malloc/free和new/delete的区别

相同点:都是从堆上开辟空间,都需要手动的进行释放!
不同之处在于:
1️⃣malloc和free是函数,而new和delete是操作符。
2️⃣malloc不会进行初始化,new开辟空间的同时可以初始化。
3️⃣malloc开辟失败是返回NULL需要我们进行判断,new开辟失败是抛出异常。
4️⃣malloc开辟空间需要手动计算大小并进行传递,而new就可以不用去计算大小!
5️⃣对于自定义类型,malloc不会去调用构造函数,free不会去调用析构函数!而new会去调用构造函数进行初始化,delete会去调用析构函数进行清理!


https://www.xjx100.cn/news/3091940.html

相关文章

为什么云游戏被认为是行业的未来趋势?

5G 时代的到来&#xff0c;游戏行业也正在经历着一场革命性的变革。云游戏&#xff0c;这个看似神秘的新兴领域&#xff0c;正在逐渐成为行业的未来趋势。 一、云游戏的优势 摆脱硬件束缚 在传统游戏中&#xff0c;玩家需要购买昂贵的游戏主机或电脑&#xff0c;才能享受高质…

如何优雅的删除HashMap元素

文章目录 1.数据准备2.删除方式2.1.使用增强 for 循环删除2.2.使用 forEach 循环删除2.3.使用 Iterator 迭代器删除2.4 使用 removeIf 删除&#xff08;推荐使用&#xff09;2.5.使用 Stream 删除&#xff08;推荐使用&#xff09; 1.数据准备 public Map<String, String&g…

面向对象程序设计1-类的定义和使用

第1关&#xff1a;数字时钟走字 任务描述 本关任务&#xff1a;本题中已给出一个时钟类的定义&#xff0c;请模拟数字时钟走字过程。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.类和对象。 编程要求 根据提示&#xff0c;在右侧编辑器补充代码&…

substring-after用法

substring-after&#xff1a;函数返回一个字符串&#xff0c;该字符串是给定子字符串后给定字符串的其余部分。 #句法 substring-after( haystack ,needle) haystack&#xff1a;要评估的字符串。该字符串的一部分将被返回。 needle&#xff1a;要搜索的子字符串。needle在h…

js实现页面滚动时自动切换Sidebar标签,点击标签自动滚动页面

js实现页面滚动时自动切换Sidebar侧边导航标签&#xff0c;点击标签自动滚动页面 <van-sidebar class"sidebar" v-model"activeKey"><van-sidebar-item :title"i.title" click"onChange(i)" v-for"(i,k) in activeList&…

关于校园网使用罗技flow功能

目录 情况概述问题及解决方案 情况概述 我目前设备是一台Macbook air m1处理器&#xff0c;学校给配了一台windows台式&#xff0c;台式机不能连蓝牙&#xff0c;不能连wifi&#xff0c;只能用网线&#xff0c;我的需求是想让mac和windows共用一套键鼠&#xff0c;在了解到罗技…

magisk 手机卡重启界面记录

中午发现手机自动关机了&#xff0c;开机后无限卡重启界面&#xff0c;但是可以进 rec 和 fastboot。怀疑是之前安装的 magisk 模块导致卡重启。不过值得一提的是&#xff0c;虽然自动关机的问题此前也出现过几次&#xff0c;但是都没有出现过卡重启的情况。 解决问题 进 rec …

【python学习】基础篇-常用模块-shutil文件和目录操作

shutil模块是Python标准库中的一个模块&#xff0c;提供了对文件和目录进行高级操作的函数。 以下是shutil模块的一些常用函数&#xff1a; 1.复制文件&#xff1a; 将源文件src复制到目标文件dst。如果follow_symlinks为True,则会跟随符号链接。 shutil.copy(src, dst, *, f…