【c++】c++内存管理
目录
- c和c++的内存分布
- 回顾C语言动态管理内存的方式
- malloc
- calloc
- realloc
- free
- C++动态管理内存的方式
- new和delete
- operator new和operator delete
- 定位new
c和c++的内存分布
回顾C语言动态管理内存的方式
malloc
void* malloc (size_t size);
malloc可以在堆上开辟指定内存的空间,参数是要开辟的空间的大小(单位字节),返回开辟好的空间的指针,注意返回的是无类型指针,需要强转一下指针类型,malloc也不会对开辟好的空间进行初始化。
calloc
void* calloc (size_t num, size_t size);
calloc同样是在堆上开辟指定内存的空间,但它要穿两个参数,num是要开辟的单元数,size是一个单元数的尺寸(单位字节),开辟的总空间大小就是num*size。calloc会对开辟的内存进行初始化,calloc同样返回无类型指针,需要强转。realloc的效率略低于malloc。
realloc
void* realloc (void* ptr, size_t size);
realloc可以更改开辟好的空间的大小,ptr给定需要改变的空间的地址,size为更改后的大小。realloc的扩容分为两种情况,原地扩和异地扩,因为开辟的空间要求是连续的,而堆上操作系统管理的内存并不一定就是连续的,当需要调整的空间后面有连续的空间且容纳的下扩容后的空间就直接原地扩容,以上条件不能满足时系统就会在另一边地方开辟size大小的空间,再把ptr上的数据拷贝过来完成拷贝。显然,原地扩的效率大于异地扩。当ptr为空指针时,realloc效果类似于malloc,当size为0时,realloc效果类似于free,但不推荐因此将realloc当malloc和free使用。calloc同样返回无类型指针,需要强转。
free
void free (void* ptr);
free可以释放动态开辟的内存,动态开辟内存后务必释放,不然会内存泄漏,一片空间只能释放一次。
C++动态管理内存的方式
new和delete
new和delete是c++中的关键字,new可以在堆上动态开辟内存并构造对象。delete可以释放new开辟的空间。使用方法如下。
int main()
{
int* a = new int;//开空间不初始化
int* b = new int(1);//开空间初始化
int* c = new int[10];//开10个int类型的空间不初始化
int* d = new int[10] {};//开10个int类型的空间初始化为0
int* e = new int[10] {0};//开10个int类型的空间初始化为0
int* f = new int[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};//开10个int类型的空间初始化为想要的数
string* g = new string;//开类类型空间不初始化(调用默认构造)
string* h = new string("Hello World");//开类类型空间初始化(调用构造)
string* i = new string[10];//开10个类类型数组不初始化(调用10次默认构造)
string* j = new string[10]{"jiunian"};//开10个类类型数组初始化了第一个(第一个调用构造,其他默认构造)
delete a;
delete b;//不是数组直接删
delete[] c;
delete[] d;
delete[] e;
delete[] f;//是数组delete后面加[]再删,注意此时delete[]为一个整体,delete[]本身为一个操作符。
delete g;
delete h;//类类型删除会调用析构函数
delete[] i;
delete[] j;//调用10次析构函数
return 0;
}
new和delete与malloc、calloc、realloc、free的区别是:
1.new和delete是操作符,而malloc、calloc、realloc、free是函数。
2.new和delete在创建类类型对象以及销毁时会调用构造和析构函数。
3.new和delete不需要计算类型大小,也不用强转指针类型,malloc、calloc、realloc、free需要。
operator new和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来释放空间的
*/
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
operator new和operator delete是系统提供的全局函数,用来开辟内存空间。操作符new的底层就是通过operator new来申请空间的,操作符delete的底层就是通过operator delete来释放空间的。需要注意的是,operator new和operator delete仍然是通过malloc和free来进行内存的开辟与释放的。另外,需要注意new的作用不只是开辟空间,它还会对空间进行初始化,而delete也不只是释放空间,它还会清空数据。这点在类类型对象的内存开辟时就显得尤为明显,new会调用构造函数,delete会调用析构函数,所以不能简单地将new与operator new、delete与operator delete划等号,他们完全不是一个东西。new与delete是操作符,用法固定;operator new、operator delete是运算符重载函数,可以被重载,自定义成想要的空间开辟方式,如果在全局域或局部域中重载operator new和operator delete(注意重载了operator new就一定要重载对应的operator delete),原来的operator new和operator delete会被覆盖·,此时在这个域中使用new和delete会以自己定义好的方式开辟内存,但在局部域中重载operator new和operator delete可以通过::new、::delete的方式使用原来的operator new和operator delete,所以重载operator new和operator delete的操作建议在局部域中完成,冒然在全局域中重载覆盖原来的operator new和operator delete会然之前写的正常版本的new和delete失效。
定位new
new (address) Type(arguments);
定位new可以在开辟好的内存空间上调用构造函数初始化一个对象,定位new不再会先分配空间再初始化,因为是使用已分配的空间,所以定位new是直接调用构造函数。定位new常被用于内存池管理等方面。具体使用方法如下。
int main()
{
std::string* buffer = (std::string*)malloc(sizeof(std::string) * 10);//开辟10个string的空间
std::string* obj = new(buffer)std::string("Hello World");//取开头的一个初始化
return 0;
}