C++内存管理方式
通过new和delete操作符进行动态内存管理
申请空间使用 new 在new之后类型即可
new T 申请单个T类型的空间
new T[N] 申请N个T类型的一段连续的空间
int main()
{
//单个
int* p1 = new int;
int* p2 = new int(10);
//连续
int* p3 = new int[10];
int* p4 = new int[10]{ 1,2,3,4,5,6,7,8,9,0 };
//释放:
delete p1;
delete p2;
//释放连续空间
delete[] p3;
delete[] p4;
return 0;
}
通过监视窗口查看
注意:一定要匹配使用 new/delete new[]/delete[] malloc//free 否则会造成内存泄漏
C++中为什么没有继续使用malloc/free
原因:C++是基于面向对象的程序设计语言,而C语言中的malloc只负责从对象申请空间,并不会调用构造方法对对象进行初始化,free只负责将空间释放掉,在释放时并不会调用析构函数清理对象中的资源。
new:在申请空间之后,也会调用构造方法将空间中的内容初始化好
delete:在释放空间前,也会调用析构函数将对象中的资源清理掉
operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
/*
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);
}
而operator delete该函数最终是通过free来释放空间的
new和delete的实现原理
问:先调用函数还是先申请空间? 答:空间
new的使用方式:T* p= new T;
调用void* operator new(size_t size)size要申请空间的总字节数 由operator new负责申请空间。 注意:该方法需要的参数是编译器编译阶段根据new之后的类型确定下来的
1.内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和 释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL。
2.自定义类型
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操作符 | 操作符new |
指的是用来申请空间的new关键字 new即是关键字(所以使用时不需要包含任何头文件),new也是一个C++中的操作符,即:new是可以重载的 | 是一个函数 void* operator new(size_t size) |
定位new表达式(placement-new)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。 使用格式: new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针,initializer-list是类型的初始化列表 。
问:为什么C++中使用了new/new[]还要使用malloc?
原因:在C++中申请空间的时候,使用的都是new或者new[],但是在特殊情况下还需使用malloc,比如实现一个内存池(一大块内存空间)。
注:malloc申请的空间肯定要比用户需要的空间大,因为还要对申请的空间进行管理
malloc实际申请的空间是 _CrtMenBlockHeader结构体的大小+用户所申请的大小+4个字节(防止越界)
为什么要实现内存池?
当用户需要申请空间的时候,不需要再去malloc了,直接从内存池中获取就可以了
只要能从该空间上执行构造方法,该块空间才可以变成完整的对象
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int,int,int)" << endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
Date* New(size_t size)
{
//申请内存空间
Date* pd = (Date*)malloc(sizeof(Date));
//指向构造方法
new(pd) Date();
return pd;
}
void Delete(Date* pd)
{
if (pd)
{
//调用析构函数清理对象中的资源
pd->~Date();
//调用free释放内存空间
free(pd);
}
}
int main()
{
//pd指向的堆空间并不是对象,因为malloc在申请空间的时候,不会调用构造方法
//而是和对象大小相同的一块内存空间
Date* pd = (Date*)malloc(sizeof(Date));
//如果可以在pd指向的空间上执行构造方法,就可以将pd指向的空间变为对象
new(pd) Date(2023,1,12);
delete pd;
pd = New(sizeof(Date));
Delete(pd);
return 0;
}
总结:定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义 类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。