C++学习之内存管理

发布于:2023-01-09 ⋅ 阅读:(427) ⋅ 点赞:(0)

1、C/C++内存分布

  1. 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  4. 数据段–存储全局数据和静态数据。
  5. 代码段–可执行的代码/只读常量。

2、C/C++内存管理方式

(1)C语言内存管理方式
在C语言中,我们开辟空间有malloc,calloc,realloc。释放空间有free
其中:
malloc - 堆上动态开辟空间
realloc - 堆上动态开辟空间 + 初始化为0 (相当于malloc + memset)
calloc - 针对已经有的空间进行扩容 (原地扩容或异地扩容)

(2)C++内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

//比如:
// 动态申请一个int类型的空间
	int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
// 动态申请3个int类型的空间
	int* ptr6 = new int[3];
//C++11支持new[] 用{}初始化  C++98不支持
	int* p5 = new int[5]{ 1,2,3 };
//删除释放
	delete ptr4;
	delete ptr5;
	delete[] ptr6;//释放数组加上一个[]

针对内置类型,new/delete跟malloc/free没有本质区别,只有用法的区别,new/delete在用法上更为简化了。
如果new一个类的空间,会在new的时候调用类的构造函数去初始化(在没有手动初始化化时)
new:1、在堆上申请空间。2、调用构造函数
delete:1、会调用析构函数清理对象中资源 2、释放空间
同样,如果new一个类的数组,会调用n次构造函数和析构函数

**结论:**new/delete 是为自动以类型准备的,不仅在堆上申请出来空间,还会调用构造和析构函数初始化和清理。

注意:
1、new/delete new[]/delete[]一定匹配使用,否则可能就出问题。
2、malloc开辟失败是返回空。而new在失败的时候,不需要检查返回值,它一旦失败会抛异常

3、operator new / operator delete函数和new和delete实现原理

注意:
operator new与operator delete函数不是重载函数,只是单纯的函数名字

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

以new为例:

底层:
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空        间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
	if (_callnewh(size) == 0)
  	{
    	// report no memory
	    // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
	    static const std::bad_alloc nomem;
 	   _RAISE(nomem);
  	}
	return (p);
}

意义:帮助new开空间。封装malloc,符合C++new的失败机制(失败抛异常)

operator delete 也是同样的原理

4、重载operator new / operator delete

注意:
一般情况下不需要对 operator new 和 operator delete进行重载,除非在申请和释放空间时候有某些特殊的需求。比如:在使用new和delete申请和释放空间时,打印一些日志信息,可以简单帮助用户来检测是否存在内存泄漏。

应用:
可以重载一个类专属的operator new

假设有一个场景要频繁的申请类(ListNode)

ListNode* node1 = new ListNode(1)如果大量的申请就比较麻烦
所以可以重载一个专属的operator new(size_t n)
同时也要重载一个专属的operator delete(void* ptr)

例如,在写顺序表的时候,如果要频繁malloc申请结点ListNode,效率太低。所以,我们可以申请ListNode时,不去malloc,而是自己定制内存池。