文章目录
C/C++内存分布
- 栈:又叫堆栈,非静态局部变量/参数函数/返回值等等,堆时可以上下增长的
- 内存映射段:是高效I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享内存,做进程间通信 - 堆:用于程序运行时动态内存分配,堆是可以向上增长的。
- 数据段:存储全局数据和静态数据。(C++不再区分bss与Data段)
- 代码段:可执行的代码/只读常量
C内存管理-malloc/free
在之前的博客:C:动态内存分配及系列函数浅析我们解释过,C语言主要使用malloc/calloc/realloc/free进行动态内存管理;
C++内存管理方式-new/delete
- C语言的内存管理方式在C++中可以继续使用,但C++也有了自己的内存管理方式:通过new和delete操作符进行动态内存管理;
- 内置类型使用实例:
void Test(){
int * ptr4 = new int;
//动态申请一个int类型的空间
int* ptr5 = new int(10);
//动态申请一个int类型的空间并初始化为10
int* ptr6 = new int[3];
//动态申请10个int类型的连续的空间
delete ptr4;
delete ptr5;
delete[] ptr6;
}
- 自定义类型使用实例:
class{
private:
int _a;
int _b;
};
int main(){
A* Pa = (A*)malloc(sizeof(A));
A* pb = new A;
delete pb;
A* pc = new A[10];
delete[] ptr6;
free(Pa);
}
- 在申请自定义类型的空间时,new会调用自定义类型的构造函数,delete会调用自定义类型析构函数,而malloc与free不会;
- 为什莫new会调用构造函数,delete会调析构函数?
- new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new&operator delete
我们来看看C++是如何实现这两个函数的:
/*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实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常,而单纯使用malloc申请失败时只会返回NULL。operator delete 最终是通过free来释放空间的
- operator new ==》malloc+异常
- operator delete : free
如何自行实现
struct ListNode{
ListNode* _prev;
ListNode* _next;
int _data;
void* operator new(size_t n){
void* p = allocator<ListNode>().allocate(1);//构建内存池
count<<"mem pool allocate"<<endl;
return p;
}
void operator delete(void* p){
allcoator<ListNode>().deallocate((ListNode*)p,1);
cout <<"mem pool dellocate"<<endl;
}
};
class List{
//friend struct ListNode;
public:
List(){
_head = new ListNode;
_head->_next = _head;
_head->_prev = _head;
}
~List(){
ListNode* cur = _head->_next;
while(cur!=_head){
ListNode* next = cur->next;
delete cur;
cur = next;
}
delete _head;
_head = nullptr;
}
private:
ListNode* _head;
}
new定位表达式
- 显式调用构造函数
- 注意:必须是已分配的原始内存空间;
class Date{
public:
Date(int year = 1900,int month = 03,int day = 25)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main(){
Date* pd = (Date*)malloc(sizeof(Date));//并未创建对象,只是有个这麽大的空间;
Date* pd2 = (Date*)malloc(sizeof(Date));
new(pd)Date;//定位表达式形式1
new(pd2)Date(1939,2,3);//定位表达式形式2
}
与malloc/free区别:
类型 | 使用 | 对自定义类型数据 | 效率 | 返回值类型 | |
---|---|---|---|---|---|
malloc | 函数 | 申请失败返回NULL,使用时需判空 | 不调构造,申请是啥就是啥不初始化 | 较高 | void*(要强转) |
new | 操作符 | 申请失败抛出异常,使用时需捕获异常 | operator new->malloc+执行构造函数完成成员变量初始化(因为构造是在申请好的空间上进行初始化,所以先申请再构造) | 较低(因为是对malloc进行了封装) | 返回申请时填的类型 |
free | 函数 | free非malloc/realloc/calloc申请空间,crash | 与malloc配对使用 | 较高 | |
delete | 操作符 | 封装free;delete最好对应new,否则不安全 | 调用析构函数+operator delete->free(因为析构函数是清理空间上的资源,所以得先清理,再释放) | 较低(同理) |
- new T[N]:调用operator new[],使其调用operator new完成n个对象的创建,再申请的空间上执行n次构造函数;
- delete[] :再释放的对象空间上执行N次析构函数,完成N个对象中资源的清理;调用operator delete[]释放空间,实际上使其调用operator delete释放空间;