【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)

简介: 【C++】深入解析C/C++内存管理:new与delete的使用及原理

【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)https://developer.aliyun.com/article/1617322


五、深入了解new和delete工作原理

new是个操作符,在编译时new A会转化为汇编指令调用malloc,一般来说malloc失败会返回空,由于C++是面向对象的过程,malloc失败返回空是不太合适,一般采用抛异常。全局函数operator new来封装malloc,去调正失败的返回情况

int main()
{
  A* p1 = new A;//operator new+1次构造
  A* p2 = new A[10];//operatorn new[]+10构造
  int* p3=new int[10];//operator new[](占用40个字节)
    
  delete p1;//1次析构+operator delete
  delete[] p2;//?次析构+operator delete
    delete[] p3;//operator delete
  return 0;
}

结合汇编和代码提供的信息,提出以下问题:

  1. 编译器如何开始确定所需开辟空间大小
  2. 为什么p2指向大小为44字节空间,而不是40字节空间
  3. 为什么编译器知道p2需要调用10次析构函数

回答:

  1. 由于new属于操作符,在编译时就计算出了所需空间的大小。
  2. 编译器在所开辟空间位置前面,也是调用operator new函数多开四个字节,用于记录对象个数(针对自定义类型)
  3. 由于内置类型不需要调用析构函数,对此不需要记录对象个数,而自定义类型需要记录对象个数。delete[]需要通过对象个数才知道调用多少次析构函数。如果将析构函数注释,p2占用空间为40字节。由于编译器会自动生成析构函数,而该析构函数没有发挥占用,编译器会优化导致不需要四个字节记录对象个数,具体需要看编译器是否优化

六、malloc/free系列和new/delete系列的区别

我们将通过用法和底层特性两点说明:

共同点:

  • 都是从堆上申请空间,并且需要用户手动释放

不同点:

  • malloc和free属于函数,new和delete属于操作符
  • malloc申请的空间不会初始化,new可以初始化
  • malloc申请空间时,需要手动计算空间并且传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  • malloc的返回值为void*,在使用事必须强转,new不需要,因为new后跟的是空间的类型
  • malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要的,但是new需要捕获异常
  • 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

七、delete最好匹配使用

解析说明:图中delete没有匹配使用,导致可能报错。这里p2指向并不是申请空间的第一个位置,第一个位置是operator   new[]实现存放对象个数申请的空间。由于空间是不能一块块释放,对此p2释放的位置是错误的,并且不明确需要调用多少次析构函数,可能会造成内存泄漏。如果是delete[]  p2,会将p2指针偏移前面四个字节。

但是以上种种情况,导致这个问题是否报错,具体需要看编译器是否进行优化(编译器是否调用析构函数),对此我们只需要正确的使用delete就行,上面只是了解就行了

八、定位new表达式(placement -new)(了解)

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

new(指针->空间)类型() :显式调用构造函数对已经有的空间初始化

构造函数不能显式调用,析构可以显式调用(一般不会去调用两次析构的)

class A
{
    public:
    A(int a = 0)
        : _a(a)
        {
            cout << "A():" << this << endl;
        }
    ~A()
    {
        cout << "~A():" << this << endl;
    }
    private:
    int _a;
}
/ 定位new/replacement new
    int main()
{
    // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
    有执行
        A* p1 = (A*)malloc(sizeof(A));
    new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
    p1->~A();
    free(p1);
    A* p2 = (A*)operator new(sizeof(A));
    new(p2)A(10);
    p2->~A();
    operator delete(p2);
    return 0;
}

一般没有人会使用,因为这里就是把new分成两部分,那么干嘛不直接使用new更加方便。

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

C++基本放弃了malloc/free系列。关于realloc扩容解决措施,在C++相关容器中它们会自动处理内存的扩容,使得开发者可以更加方便地使用动态大小的数据集合。

九、内存泄漏(了解)

9.1 内存泄漏概念

内存泄漏指因为疏忽或者错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费

9.2 内存泄漏的危害

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

9.3 内存泄漏分类

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

1.堆内存泄漏(Heap leak)

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

2.系统资源泄漏

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


以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!

相关文章
|
9月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
322 26
|
存储 程序员 编译器
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
10月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
183 1
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
257 4
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
475 12
|
10月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
254 0
|
10月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
403 0
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)

热门文章

最新文章

推荐镜像

更多
  • DNS