CC++内存管理
Posted 雨轩(爵丶迹)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CC++内存管理相关的知识,希望对你有一定的参考价值。
C/C++内存管理
1、C/C++内存分布
我们先来看内存区域的一个划分
-
栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
-
内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
-
堆用于程序运行时动态内存分配,堆是可以上增长的。
-
数据段–存储全局数据和静态数据。
-
代码段–可执行的代码/只读常量。
- 常规描述:
-
全局变量和静态变量都存储与数据段
-
常量字符串(只读常量)和可执行代码位于代码段
-
动态开辟的位于堆中
-
局部变量位于栈帧中
2、C语言中的动态内存管理方式
之前我们在C语言中已经知道malloc/calloc/realloc/free,我们再来复习一下malloc/calloc/realloc的区别。
malloc在堆区中开辟一块连续的内存,返回一个指向被分配的内存块起始位置的指针。返回的类型是不知道的,返回一个类型为void *的指针,返回指向内存的指针不会初始化。
calloc跟malloc类似,主要区别是calloc在返回指向内存的指针之前会把它初始化为0。
realloc函数用于修改一个已经分配的内存块的大小。
- 在原先的内存块够大时,如果它用于扩大一个内存块,那么原来的内存块的内容依然保留,新增加的内存添加到原来内存块的后面。如果它用于缩小一个内存块,该内存块尾部的内存将被删除,剩余部分内存的原来的内容依然保留
- 如果原先内存不够用了,则开辟一块新的内存,并将原理那块的内存的内容复制到新的内存块上(我们这里也要注意需要手动去删除自己原来开辟的内存块)
int* a = (int*)malloc(sizeof(int));
printf("%d\\n", *a);
free(a);
int* b = (int*)calloc(4, sizeof(int));
printf("%d", *b);
int* c = (int*)malloc(sizeof(int));
int* d = (int*)realloc(c, sizeof(int) * 40);
free(d);//是否需要释放c,根据实际的情况而定
3、C++内存管理方式
void Test()
{
// 动态申请一个int类型的空间 未初始化
int* ptr4 = new int;
cout << *ptr4 << endl;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
cout << *ptr5 << endl;
// 动态申请10个int类型的空间 管理对象数组
int* ptr6 = new int[10];
for (int i = 0; i < 10; i++)
{
ptr6[i] = i;
cout << ptr6[i] << " ";
}
cout << endl;
delete ptr4;//释放对象方式
delete ptr5;
delete[] ptr6;//释放对象数组方式
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]
4、malloc/free/new/delete的区别
class Test
{
public:
Test()
: _data(0)
{
cout << "Test():" << this << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
void Test2()
{
//内置类型 这里malloc和new没有什么区别
int* a = (int*)malloc(sizeof(int));
int* b = new int(5);
free(a);
delete(b);
//自定义类型 用new、delete还会调用构造函数和析构函数,malloc和free不会
// 申请单个Test类型的空间
Test* p1 = (Test*)malloc(sizeof(Test));
free(p1);
// 申请10个Test类型的空间
Test* p2 = (Test*)malloc(sizeof(Test) * 10);
free(p2);
// 申请单个Test类型的对象
Test* p3 = new Test;
delete p3;
// 申请10个Test类型的对象
Test* p4 = new Test[10];
delete[] p4;
}
图示,我们可以看出几点:
- 对于内置类型,用malloc和new没有太大的区别
- 对于自定义类型,malloc、free和new、delete有很大的区别,new会调用构造函数,delete会调用析构函数,而malloc和free不会
5、operator new 与 operator delete函数
- new和delete是用户进行动态内存申请和释放的操作符
- operator new 和operator delete是系统提供的全局函数
- new在底层调用operator new全局函数来申请空间,delete在底层通operator delete全局函数来释放空间
- operator new:实际是通过malloc来申请空间,申请成功直接返回,申请失败了会去指向空间不足的应对措施,如果用户设置了相应的应对措施,则继续申请,或者会抛异常(异常后后面在说)
- operator delete:该函数最后是通过free去释放空间的
/*
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)
总的来说:new和delete就是经过C++一定的装饰,其本质还是离不开malloc和free。
6、new和delete的实现原理
上面我们已经剖析了new和delete的原理,我们这里在总结下
-
对于内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
-
对于自定义类型
-
new的原理
- 调用operator new函数申请空间
- 在申请的空间上调用构造函数
-
delete的原理
- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
-
new T[N]的原理
-
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
-
在申请的空间上执行N次构造函数
-
-
delete[]的原理
-
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
-
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
-
-
7、定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象 。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化
class Test
{
public:
Test()
: _data(0)
{
cout<<"Test():"<<this<<endl;
}
~Test()
{
cout<<"~Test():"<<this<<endl;
}
private:
int _data;
};
void Test()
{
// pt现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
Test* pt = (Test*)malloc(sizeof(Test));
new(pt) Test; // 注意:如果Test类的构造函数有参数时,此处需要传参
}
好久没更新了,更新一波
以上是关于CC++内存管理的主要内容,如果未能解决你的问题,请参考以下文章