• 初识C++内存管理


    目录

    1. new 和delete

    new 一个对象

    初始化

    new 多个对象

    初始化 

    delete 释放

    C++内存开辟释放和C中开辟释放有何区别? 

    2. operator new与operator delete函数

    2.1 operator new

    和malloc相同点

    和malloc不同点

    new底层原理

    2.2 operator delete

    2.3 operator new与operator delete的类专属重载

    3. 定位new表达式

    使用格式

    定位new什么时候用?


    在C语言中,动态内存中我们用 malloc/calloc/realloc 等来在堆上开辟空间,free 来释放,在C++中,不仅支持C语言的方式,还确定了自己的方式。本篇我们来记录C++是如何进行内存管理的。


    1. new 和delete

    在堆上开辟空间,C语言中我们要用到malloc,并且使用方式较为繁琐:

    ( int* )malloc(sizeof( int ) * 4)

    且calloc/realloc 的方式也都相同,详细点击这里:动态内存管理分析理解

    而在C++中使用 new 可以简化使用方式:

    new 一个对象

    new + 类型

    int* p1 = new int;

    初始化

    new + 类型后面+(值)

    int* p1 = new int(5);

     

    new 多个对象

    new + 类型 [个数]

    int* p2 = new int[10];

    初始化 

    new + 类型 [个数] {初始化的值}

    int* p3 = new int[10]{1, 2, 3, 4, 5, 6};

     

    delete 释放

    要注意格式匹配

    像单个对象,直接delete 即可

    delete p1;

    如果是多个对象,delete + []

    1. delete[] p2;
    2. delete[] p3;

    C++内存开辟释放和C中开辟释放有何区别? 

    对于内置类型来说:new 和 malloc 并没有什么区别,只是用法不同;

    对于自定义类型来说,new更加方便,会自动调用其构造函数,相当于开辟空间加初始化:

    而利用初始化列表,创建构造函数,使用new ,可以大大简化这些步骤:

    delete和free 对内置类型的处理方式也没什么区别

    但是对于自定义类型而言

    delete 对于自定义类型是会调用它自己的析构函数:

    2. operator new与operator delete函数

    这两个函数不是new 和 delete 的重载,只是名字的区别

    new和delete是用户进行动态内存申请和释放的操作符

    operator new 和operator delete是系统提供的全局函数

    new在底层调用operator new全局函数来申请空间

    delete在底层通过operator delete全局函数来释放空间

    2.1 operator new

    实际上operator new函数是malloc函数封装后的成果

    此函数不会调用构造函数

    和malloc相同点

    都是函数、都会开辟空间

    使用方法和malloc相同

     

    和malloc不同点

    通过malloc来申请空间,当malloc申请空间成功时直接返回,

    申请空间失败,不会返回空,会抛异常;

    这一点就跟new相同了


    new底层原理

    开辟空间,失败抛异常 + 调用构造函数

    这两个功能合并到一块就构成了new

    而开辟空间失败抛异常其实调用的就是封装malloc后的operator new函数。

    operator new + 调用构造函数 = new

    2.2 operator delete

    实际上operator delete函数是free函数封装后的成果

    此函数不会调用析构函数

    为了跟operator new匹配对应

    operator delete + 调用析构函数 = delete

    2.3 operator new与operator delete的类专属重载

    在项目中往往存在一直需要开辟空间的时候,就比如在链表中插入数据,每次new一个节点,都会在堆上申请,但是如果数据量较大,那么效率是会变低的,来看一个测试:

    1. struct Listnode
    2. {
    3. Listnode* _next;
    4. Listnode* _prev;
    5. int _data;
    6. Listnode(int data = 0)
    7. :_next(nullptr)
    8. , _prev(nullptr)
    9. , _data(data)
    10. {
    11. //记录节点数
    12. cout << "Listnode" << endl;
    13. }
    14. };
    15. class List
    16. {
    17. public:
    18. List()
    19. {
    20. _head = new Listnode;
    21. _head->_next = _head;
    22. _head->_prev = _head;
    23. }
    24. void Push(int val)
    25. {
    26. //每一次插入数据,都需要new一个节点
    27. Listnode* newnode = new Listnode;
    28. Listnode* tail = _head->_prev;
    29. //链接
    30. tail->_next = newnode;
    31. newnode->_prev = tail;
    32. newnode->_next = _head;
    33. _head->_prev = newnode;
    34. }
    35. ~List()
    36. {
    37. Listnode* cur = _head->_next;
    38. while (cur!=_head)
    39. {
    40. Listnode* next = cur->_next;
    41. delete cur;
    42. cur = next;
    43. }
    44. delete _head;
    45. _head = nullptr;
    46. }
    47. private:
    48. Listnode* _head;
    49. };
    50. int main()
    51. {
    52. List l;
    53. int n = 1000;
    54. for (int i = 0;i<n;i++)
    55. {
    56. l.Push(i);
    57. }
    58. }

    为了提高速度,创建operator new与operator delete的类专属重载;

    在构造节点时,会自动调用operator new的重载,清空时自动调用与operator delete的重载;

    实现这两个重载函数,用到的是内存池,使用内存池申请和释放内存,提高效率

    在之后会了解到,这里先主要是以重载为主,稍作了解:

    1. struct Listnode
    2. {
    3. Listnode* _next;
    4. Listnode* _prev;
    5. int _data;
    6. Listnode(int data = 0)
    7. :_next(nullptr)
    8. , _prev(nullptr)
    9. , _data(data)
    10. {
    11. //记录节点数
    12. cout << "Listnode" << endl;
    13. }
    14. void* operator new(size_t n)
    15. {
    16. void* p = nullptr;
    17. p = allocator<Listnode>().allocate(1);
    18. cout << "memory pool allocate" << endl;
    19. return p;
    20. }
    21. void operator delete(void* p)
    22. {
    23. allocator<Listnode>().deallocate((Listnode*)p, 1);
    24. cout << "memory pool deallocate" << endl;
    25. }
    26. };

    3. 定位new表达式

    operator new 函数是调用 new 时的一部分,那我们单独调用operator new 进行空间开辟时,又不好初始化,因为构造函数不能显示调用。

    使用格式

    new (指针) + 类型;

    new (指针) + 类型(初始化列表);

     

    定位new什么时候用?

    那么我们直接调用 new 不香吗,非得用operator new + 定位new?

    定位new表达式在实际中一般是配合内存池使用:

    因为内存池分配出的内存没有初始化,

    所以如果是自定义类型的对象,

    需要使用new的定义表达式进行显示调构造函数进行初始化

     

  • 相关阅读:
    Postgresql自定义聚合函数入门案例
    Spring Boot 之 SpringSecurity、Shiro
    专精特新!2024年湖北省专精特新中小企业申报奖励、申报条件整理
    Hadoop:Hive操作(二):数据表操作,复杂数据类型,Sampling采样,虚拟列
    AKKA 互相调用
    [Vue 代码模板] Vue3 中使用 Tailwind CSS + NutUI 实现侧边工具栏切换主题
    Map,Set和哈希表的使用
    mysqlclient在django启动时报错: Did you install mysqlclient?
    js基础知识整理之 —— 闭包
    108.网络安全渗透测试—[权限提升篇6]—[Windows内核溢出提权]
  • 原文地址:https://blog.csdn.net/weixin_53316121/article/details/125019380