文章目录
C/C++编译的程序占用内存主要分为以下几个部分:
- 数据段:存放全局变量和静态变量,程序运行时分配,程序结束时释放;
- 代码段:存放函数体和只读常量,程序运行时分配,程序结束时释放;
- 堆:由程序猿主动分配与释放,若程序猿没有主动释放则程序结束时自动释放;
- 栈:存放为运行函数时分配的局部变量、函数参数、返回数据、返回地址等,由编译器自动分配与释放,函数运行开始时分配、结束时释放;
内存分布简易图:
C语言动态内存的管理方式
使用malloc、realloc、calloc来申请堆上的内存,使用free释放堆上的内存。
之前的博客总结过C语言动态内存管理的方式,下面是链接。
C++动态内存的管理方式
使用操作符new来申请内存,使用操作符delete来释放内存。
操作内置类型
申请单个空间
//new是操作符,并不是函数,所以new申请空间时,后面不需要加()
int* pi = new int;//申请一块四字节空间,返回int*指针
char* pc = new char;//申请一块一字节空间,返回char*指针
申请单个空间时初始化
//在类型后面加()表示初始化,里面填写初始化的内容
int* pi = new int(10);//申请一块四字节空间,并初始化为10,返回int*指针
char* pc = new char('a');//申请一块一字节空间,并初始化为a,返回char*指针
释放单个空间
delete pi;//释放pi所指空间
delete pc;//释放pc所指空间
申请多个连续空间
//用[]来表示申请多个连续空间,里面填写空间个数
int* pni = new int[10];//申请十块连续的四字节空间,返回第一块的地址为int*指针
char* pnc = new char[5];//申请五块连续的一字节空间,返回第一块的地址为char*指针
申请多个连续空间并初始化
//用{}来表示初始化多个连续空间,若是不完全初始化,则未初始化部分默认为0
int* pni = new int[10]{0,1,2,3,4,5,6,7,8,9};
char* pnc = new char[5]{'a','b','c'};
释放多个连续空间
delete[] pni;//释放pni所指的连续空间
delete[] pnc;//释放pnc所指的连续空间
操作自定义类型
操作方法跟操作内置类型一模一样,只是对自定义类型来说,new会自动调用构造、delete会自动调用析构。
new失败时会抛异常,而不是返回nullptr(malloc失败会返回nullptr)
operator new和operator delete
operator new 和 operator delete是两个函数,而不是运算符重载(本身new和delete就不是运算符)!!
new和delete是C++引入的操作符,operator new和operator delete是系统提供的全局函数。
new在底层调用operator new来申请资源,operator new实际也是在用malloc申请资源;
delete在底层调用operator delete来释放资源,operator delete实际也是在用free释放资源。
//operator new
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
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的实现原理
对于内置类型
new、delete和malloc、free大致相似,但有一些不同点:
- new和delete申请、释放单个空间,new[]和delete[]申请、释放多个连续空间;
- new和new[]在申请空间失败时会抛异常而不是返回nullptr;
对于自定义类型
new会自动调用构造,delete会自动调用析构
大致流程如下:
new:先调用operator new申请内存,然后再调用构造函数;
delete:先调用析构函数,然后再调用operator delete释放内存;
new[]:先调用operator new[]申请内存,operator new[]会根据次数做标记,然后根据标记调用operator new直接连续开够,再根据标记连续调用构造函数;
delete[]:根据标记连续调用析构函数,然后根据标记调用operator delete释放内存;
定位new表达式
定位new表达式是在以分配的原始内存空间中调用构造函数初始化一个对象
格式:
new(place_address)type或者new(place_address)type(initializer_list)
place_addreee
必须是一个指针,initializer_list
是类型的初始化列表
使用场景:
定位new表达式一般搭配内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定位表达式显示调构造函数进行初始化。
#include <iostream>
using namespace std;
class A{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main() {
A* p1 = (A*)malloc(sizeof(A));//p1所指内存无初始化
new(p1)A;//定位new表达式,这里没有传初始化参数
delete p1;
A* p2 = (A*)malloc(sizeof(A));//p2所指内存无初始化
new(p2)A(10);//定位new表达式,这里传了初始化参数
delete p2;
return 0;
}
malloc、free与new、delete的联系与区别
共同点:都需要手动申请内存(在堆上),并且需要手动释放内存;
不同点:
- malloc和free是函数。new和delete是操作符;
- malloc申请的空间不会初始化。new申请的空间会初始化(对内置不做处理,对自定义调用构造);
- malloc申请空间时需要手动计算大小并传递。new只需要在后面跟上类型,若是多个对象只需要在[]中指定数量;
- malloc的返回值为void*,必须进行强制类型转换。new不需要,因为new后跟的是空间的类型;
- malloc申请失败时会返回nullptr,因此使用时必须进行判断。new申请失败会抛异常;
- 申请自定义类型空间时,new和delete会自动调用构造和析构。