C++内存管理(下)

简介: C++内存管理

Ⅳ new 返回异常


如果开辟失败,在C语言阶段会返回一个nullptr(空指针),而C++则会抛出异常。


1669269977938.jpg


malloc开辟失败会返回nullptr。


1669269987718.jpg


new开辟失败抛出异常。


三、operator new和operator delete函数


new和delete真的那么神奇吗?其实它们是通过调用两个全局函数来实现开辟空间和释放空间的。


new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new 全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。(这里要注意operator new不是new的重载!!)


所以,用C封装的new和delete实际图解就包括这两部分:


1669269998785.jpg


我们通过编译器也可以看出来,new的底层确实是这样实现的:


1669270009039.jpg


那么,operator new和operator delete又有什么猫腻呢?


Ⅰ operator new和 malloc


operator new:该函数实际通过malloc来申请空间,它的用法和malloc很相似,不一样的地方,在于开辟失败,malloc返回nullptr而operator new函数则抛出异常。


1669270024749.jpg


同理,operator delete函数则是通过调用free来实现释放空间的。


总结一下malloc/free和new/delete的区别


malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:


1、malloc和free是函数,new/delete是操作符。


2、malloc申请的空间不会初始化,new可以初始化。


3、malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[ ]中指定对象个数即可。


4、malloc的返回值是void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型。


5、malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但new需要捕获异常。


6、申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。


三、定位new表达式(placement-new)


定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。


Ⅰ使用格式


new(place_address) type 或者new(place_address) type(initializer-list)


place_address必须是一个指针,initializer-list 是类型的初始化列表。


Ⅱ使用场景


定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显式构造函数。


1669270056837.jpg


malloc开辟的一块空间,进行显式调用构造函数初始化,这时候p3所指向的空间就可以认为是new开辟的空间,可以delete。如果对malloc开辟的空间没有定向new,也可以delete,但是会有警告。


Ⅲ 定向new的意义


有的老铁可能有困惑,为啥要有定向new,直接new初始化不就可以了?这不是脱裤子放屁,多此一举吗?


既然设计了,那么肯定有它的使用场景的。定向new主要用于池化技术,可以很好的提高效率。什么是池化技术呢?


我们一般无论是malloc还是new都是去堆上申请空间,类似于以前大家要用水都要去河边打水。而内存池就相当于我们自己在家里做一个蓄水池(malloc 一块空间),用水就可以直接使用(定向new)。这样会提高效率,我们不需要去挤着都去河边打水。类似于各项需要向堆申请空间的工作都被分配给一定空间,使用时各自去使用这么一块空间,每一块空间就是一个内存池供不同的工作使用,提高了工作效率。


1669270068618.jpg


四、内存泄漏


Ⅰ什么是内存泄漏


内存泄漏指因为疏忽或错误造成程序未被释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,不再使用内存时由于没有返还给操作系统并且失去对该段内存的控制,导致内存消耗越来越大,直到系统崩溃。


C/C++程序中一般我们关心两种方面的内存泄漏:


🖊堆内存泄漏(Heap leak)


堆内存指的是程序执行中依据需要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak


🖊系统资源泄漏


指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。


Ⅱ内存泄漏的情景


我们可以看一下程序执行过程中,如果没有写释放空间会发生什么:


1669270085375.jpg


看到这里,我们发现,咦,我没有释放空间,但是程序结束它把申请的空间返还给操作系统了啊,内存泄漏好像没有什么危害吖。


但是联系现实,程序结束返还操作系统,如果程序不结束呢?比如长期运行的系统,服务器,比如我们玩的游戏服务器就是长期运行不关机的。这时如果有内存泄漏就会导致可用内存越来越少,它的表现就是cpu发热,电脑很卡,直到系统崩溃。


有时候有这样的场景,我们写了释放空间,但是可能执行不到这一步:


1669270101603.jpg


我们知道C++会抛出异常,如果在释放空间之前抛出异常,导致内存没有泄漏,这是不易发现且易于出错的地方。


最恐怖的不是一下子服务器挂掉,而是内存一点一点泄漏,每次泄漏一点内存,不易发现,可能过了十几天才发现服务器越来越卡,比较明显的例子就是早期的Android就是长期运行会变得越来越卡,可能就存在内存泄漏。


C++针对这些情况,官方设计了智能指针事前预防,也有一些第三方推出的内存泄露检测工具,用于事后差错。


第三方内存泄漏检测工具:


       在Linux下内存泄漏检测:Linux下几款内存泄漏检测工具


       在Windows下使用第三方工具:VLD工具说明


       其他工具:内存泄漏工具比较


Ⅲ如何避免内存泄漏


1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放,不过可能碰到抛出异常导致走不到释放。

2. 采用RAII思想或者智能指针来管理资源。

3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

总的来说就两种解决方案:1、事前预防:智能指针

                                          2、事后差错:内存泄漏检测工具

相关文章
|
2月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
120 26
|
7月前
|
存储 程序员 编译器
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
3月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
52 1
|
9月前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
385 68
|
6月前
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
7月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
318 0
|
8月前
|
存储 程序员 编译器
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
287 0
|
10月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
170 3
|
10月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
531 4
|
11月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。