• C/C++内存管理(一)---->new和delete


    主要内容:

    • C/C++内存布局
    • new和malloc的区别
    • delete和free的区别

    C/C++内存布局
    C/C++内存分为如下的几个部分:

    1.栈区 存放临时的局部变量
    2.堆区 malloc,realloc,calloc申请的动态内存
    3.静态区 存储全局变量和局部静态变量
    4.常量区(代码段) : 存储常量的区域

    首先我们要知道为什么要对内存区域进行划分。最重要的一个原因就是为了方便管理数据,因为计算机世界最重要的资源就是数据。对于数据更好 的管理就显得十分有必要!而划分内存区域就是这样的一种手段。
    来看一看下面这些变量分别存储在什么地方:

     #define _CRT_SECURE_NO_WARNINGS 1
    #include<iostream>
    using namespace std;
    static int g_val = 10;
    void exam()
    {
    	static int s_val = 20;
    	int val = 5;
    	char arr[] = "hello world";
    	const char* pa = "hello world";
    	int* a = (int*)malloc(sizeof(int) * 5);
    	free(a);
    }
    int main()
    {   
    	exam();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    g_val ,s_val ,val在什么区域?
    arr在哪个区域,*arr在什么区域?
    p在哪个区域,*p在哪个区域?
    a在哪一个区域,*a在哪个区域?

    答案是:g_val ,s_val在静态区,arr,*arr在栈区
    p在栈区,*p在常量区 , a在栈区,a在堆区

    可能比较容易出错的就是arr和arr这两个地方

    char arr[]="abcdef";
    //abcdef是一个常量字符串,本身是在静态区,但是这段语句的意思是创建一个数组,把常量字符串"abcdef"的内容拷贝到这个数组里面,所以这个数组本质还是在栈上的!
    
    • 1
    • 2

    了解了这个C/C++的内存布局,接下来我们来看new和malloc的区别。


    new和malloc的区别
    首先,我们知道C++兼容C语言,也就是C语言那套内存管理机制我们在C++里面仍然还可以继续使用。但是C++又引入了自己的新机制—>new关键字来替代malloc,那么new和malloc究竟有什么区别。new又应该怎么使用呢?

    //new的使用
    void test()
    {   //动态申请一个int类型的对象
    	int* p = new int;
    	//动态申请一个能够存放10个int的数组
    	int* pa = new int[10];
    	//和第二个区别,这个只申请了一个int类型对象,这个对象的值被初始化成10
    	int* ppa = new int(10);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    那么这时候不难可以看出。这里的new和malloc并没有太大的区别,确实是这样!对于内置类型来说,使用new和使用malloc几乎没有区别。真正不同的地方是在于对于自定义类型的处理!

    class A
    { 
      public:
    	A(int a=0)
    	:_a(a)
        {}
      private:
    	int _a;
    };
    //new和malloc的区别--->自定义类型
    void show()
    {
    	//使用malloc创建A类成员
    	A* pa = (A*)malloc(sizeof(A));
    	//使用new来创建A类成员
    	A* ppa = new A;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    从调试窗口不难可以看出,malloc只是申请了一块空间,对于空间里面的内容不做任何处理!而new在申请空间的同时,还还调用了构造函数!所以对于自定义类型来说,new会在申请空间的同时调用构造函数初始化! 那么接下来我们用一个实例体会new带来的好处。

    //使用new来创建带头双向循环链表
    struct ListNode
    {
    	int _val;
    	ListNode* _prev;
    	ListNode* _next;
    	//写好ListNode的构造函数
    	ListNode(int val=0)
    		:_val(val)
    		,_prev(nullptr)
    		,_next(nullptr)
    	 {}
    };
    class List
    { public:
    	List()
    	{ //自动就会调用ListNode的构造函数(这里是默认构造)
    		_head = new ListNode;
    // _head=new ListNode();--->这种写法也可以,不推荐!(严格和Java的写法区分开来!)
    			
    		_head->_prev = _head;
    		_head->_next = _head;
    	}
    	~List()
    	{
    		ListNode* cur = _head->_next;
    		while (cur != _head)
    		{
    			ListNode* next = cur->_next;
    			delete cur;
    			cur = next;
    		}
    		delete _head;
    		_head = nullptr;
    	}
     private:
    	 ListNode* _head;
    };
    int main()
    {
       List L;
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    我们只要在main里面写这样一个语句就可以创建一个带头双向循环链表了!相比于先前使用C语言写一大堆冗余的函数来进行工作,一个new就为我们不少的问题。
    顺便补充一下,使用vs开发的时候,vs会强制性检查malloc,realloc,calloc动态开辟以后返回的指针的合法性,而使用new就不会检查,因为new失败以后不是返回空指针,而是会抛出异常。关于异常,我会在后续的博客内容中介绍,这里只要了解一下new失败是抛出异常就可以了。


    2.delete和free的区别
    C++也提供了特定的内存释放的方式---->delete,那么首先我们先来看一看delete是怎么使用

    //delete的使用
    int main()
    {  //申请1个int的空间 
       int* p=new int;
       //释放p所指向的1个int的空间
       delete p;
       //申请10个int的空间
       int* pp=new int[10];
       //释放pp指向的连续空间
       delete[] pp;
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意:new就要和delete配对!malloc,realloc,calloc就要和free一起!new数组就要用delete[]释放 。不要混着乱用!乱用可能有的时候不会出错,但是多数情况下可能会导致程序崩溃!
    那么和C++引入new一样,C++引入delete来管理内存的释放肯定是有delete的独到之处的,我们不妨来看一下经典的MyQueue类,看看free和delete一个MyQueue对象是一种什么样的场景:

    class Stack
    {
    public:
    	Stack(int capacity=4)
    		:_a(new int[capacity])
    		,_size(0)
    		,_capacity(capacity)
    	{}
    	~Stack()
    	{
    		delete[] _a;
    		_a = nullptr;
    	}
    private:
    	int* _a;
    	size_t _size;
    	size_t _capacity;
    };
    class MyQueue
    {
    private:
    	Stack _pushst;
    	Stack _popst;
    };
    int main()
    {   MyQueue* p=new MyQueue;
        delete p;
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述
    此时还没执行delete,单步按下F11进入下一步调用的函数内部
    在这里插入图片描述
    可以看到,delete并不是直接就把p指向的那块空间释放了,而是先调用了析构函数清理资源!最后清理完资源以后才会释放p指向的空间!而如果是free,free就只会释放当前指针指向的那块空间,极大可能会造成内存泄漏!


    总结

    • new不仅会申请空间,还会调用构造函数
    • delete在释放指针指向空间之前,会先调用析构函数清理资源。

    文章主要内容就到这里,希望大家可以共同进步。

  • 相关阅读:
    Java表达式及Java块
    Nginx缓存
    I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
    前端框架 网络请求 Fetch Axios
    Java内部类
    Oncommand解析代码
    ChatGPT付费创作系统V2.4.9独立版 +WEB端+ H5端 + 小程序端系统测试安装教程
    java 工程管理系统源码+项目说明+功能描述+前后端分离 + 二次开发
    【Spark NLP】第 11 章:词嵌入
    《Spring Boot配置文件大揭秘:看懂 application.yaml 与 bootstrap.yaml 的不同》
  • 原文地址:https://blog.csdn.net/qq_56628506/article/details/125529588