• C++内存管理


    一、C/C++内存分布

    通过下面程序在Linux环境下验证C语言的内存分区

    #include
    #include
    
    int g_val1 = 1;
    int g_val2 ;
    int main()
    {
    	printf("code_addr:%p\n", main);
    	const char* str = "hello world";
    	printf("read_only:%p\n", str);
    	printf("i n i t  :%p\n", &g_val1);
    	printf("un _ init:%p\n", &g_val2);
    
    	char* p1 = (char*)malloc(10 * sizeof(char));
    	char* p2 = (char*)malloc(10 * sizeof(char));
    	char* p3 = (char*)malloc(10 * sizeof(char));
    	printf(" h e a p :%p\n", p1);
    	printf(" h e a p :%p\n", p2);
    	printf(" h e a p :%p\n", p3);
    
    	printf("s t a c k:%p\n", &str);
    	printf("s t a c k:%p\n", &p1);
    	printf("s t a c k:%p\n", &p2);
    	printf("s t a c k:%p\n", &p3);
    
    	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

    在这里插入图片描述

    二、C语言中的动态内存管理

    C语言动态内存管理:malloc/calloc/realloc
    Windows中提供的内存泄漏检测函数:_CrtDump_MemoryLeaks()
    在这里插入图片描述

    三、C++动态内存管理

    C++中兼容C语言的动态内存管理方式,同时也提出了新的内存管理方式:通过new和delete操作符进行动态内存管理

    new和delete的使用

    1.申请单个int类型空间

    	int* pt = new int;
    	delete pt;
    
    • 1
    • 2

    2.申请连续空间

    	int* pa = new int[5];
    	delete[] pa;
    
    • 1
    • 2

    3.申请连续空间并赋值

    	int* pa2 = new int[5]{ 1,2,3,4,5 };
    	delete[] pa2;
    
    • 1
    • 2

    new和delete对自定义类型的操作

    在这里插入图片描述
    申请自定义类类型的空间时,new会调用构造函数,delete会调用析构函数,malloc和free不会

    operate new和operate delete

    new和delet函数是用户进行动态内存申请和释放的操作符,operate new和operate delete是系统提供的全局函数,new是在底层调用operate new全局函数来实现的,delete在底层通过调用operate delete全局函数来释放空间。

    new的底层实现原理

    Date* p1 = new Date;
    
    • 1

    在这里插入图片描述
    可以看到new操作符申请类类型空间时,会先调用该类的构造函数,然后调用operate new函数。而operate new函数在底层调用malloc函数,如图:
    在这里插入图片描述
    对于operate new函数,在while循环中,通过malloc循环从堆上申请空间,如果申请成功,则返回空间的首地址,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛出异常。所以operate new一旦返回了,在程序中拿到的肯定是有效堆空间.

    delete底层实现原理

    delete p1;
    
    • 1

    在这里插入图片描述
    delete在删除类类型对象申请的空间时,会先调用析构函数清理资源,再调用operate delete函数。operate delete函数在底层会调用free函数释放空间,如图:
    在这里插入图片描述

    总结

    对于内置类型,malloc和new,delete和free基本相似,不同的地方是:new[]和delete[]申请释放的是连续空间,new在申请失败时会抛出异常,malloc会返回NULL

    new的原理

    1. 调用operator new函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造

    delete的原理

    1. 在空间上执行析构函数,完成对象中资源的清理工作
    2. 调用operator delete函数释放对象的空间

    new T[N]的原理

    1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
    2. 在申请的空间上执行N次构造函数

    delete[]的原理

    1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
    2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

    定位new表达式

    定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
    使用格式:
    new (place_address) type或者new (place_address) type(initializer-list)
    place_address必须是一个指针,initializer-list是类型的初始化列表
    使用场景:
    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

    void Test()
    {
    	Date* pt = (Date*)malloc(sizeof(Date));
    	new(pt) Date; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    调用malloc函数申请内存后,pt指向了一段和Test对象相同大小的空间,此时没有调用构造函数,所以并没有创建对象,使用定位new表达式会调用构造函数,创建对象。

    四、面试题

    malloc/free和new/delete区别

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

    1. malloc和free是函数,new和delete是操作符
    2. malloc申请的空间不会初始化,new可以初始化,如:int *pt=new int(10)
    3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
    4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
    5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
    6. 申请自定义类型对象时,malloc/free只会开辟空间,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

    内存泄漏及其危害

    • 概念:内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况,失去了对某一内存段的控制,因而造成的内存资源浪费。
    • 危害:对于长期运行的程序出现内存泄漏,影响很大,如操作系统,杀毒软件,后台服务等,出现内存泄漏会导致响应越来越慢,最终卡死。

    如何避免内存泄漏:事前预防,如使用智能指针,事后查错型,使用内存泄漏检测工具。

    如何一次在堆上申请4G的内存

    int main()
    {
    	void* p = new char[0xfffffffful];
    	cout << "new:" << p << endl;
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ul:unsigned long的简写,表示无符号长整型
    在这里插入图片描述

  • 相关阅读:
    split() 函数实现多条件转为数据为数组类型
    RS485电工详解
    Spring 理解
    SQL排序函数详解+案例实战
    如何做大你的软件研发团队?
    rocketMQ高级和源码
    Prompt工程师指南[应用篇]:Prompt应用、ChatGPT|Midjouney Prompt Engineering
    BeanPostProcessor和BeanFactoryPostProcessor简单介绍
    【论文精读】Chain-of-Thought Prompting Elicits Reasoning in Large Language Models
    实验跟踪
  • 原文地址:https://blog.csdn.net/qq_44631587/article/details/126103823