C++ (8)C/C++动态内存管理
(一)程序内存空间(代码段、数据段、堆、栈段)
在冯诺依曼的体系结构中,一个进程必须有:代码段,堆栈段,数据段。
(二)C语言动态内存管理
- C语言使用malloc/calloc/realloc/free进行动态内存管理
malloc()函数
void* malloc(size_t size)
(typedef unsigned int size_t)
malloc在内存中分配一个长度为size字节的动态存储区,其参数是无符号整形,返回一个指向所分配的连续空间的起始地址的指针
calloc()函数
void* calloc(size_t n, size_t size)
在内存中动态的分配n个长度为size的连续空间,并将每一个字节都初始化为零
realloc()函数
void* realloc(void* ptr, size_t size)
当需要扩大一块内存空间时,realloc试图直接从堆的当前内存段后面获取更多的内存空间,并返回原指针。
如果空间不够,就使用第一个能够满足这个要求的内存块,并将当前数据写到新位置,释放原来的数据块。
这里为什么不需要free(p2)?
p2指针与p1指针指向一致
(三)C++动态内存管理
- C++通过
new
和delete
动态管理内存 new/delete
动态管理对象new[]/delete[]
动态管理对象数组
内存管理
C++是兼容C的,那么已经有C库函数malloc/free等来动态管理内存,为什么C++还要定义new/delete运算符来动态管理内存?
对象在创建的同时要自动执行构造函数,对象消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能将执行构造函数与析构函数的任务强加于malloc/free.
如果执行:Array *p1 = (Array*)malloc(sizeof(Array));
不调用构造函数;Array *p2 = new Array;
调用构造函数
C++的其他内存管理接口(placement版本)
在C++编程语言中,placement语法使得程序员能够明确地指定单个对象的内存管理,比如,他们在内存中的“位置”。new, delete操作符以及函数的”placement”版本被称为placement new和placement delete.
一个new表达式,placement或者其他叫做new函数,也被称为内存分配函数,或者new操作符。同样的,一个delete表达式叫做delete函数,也被称为内存释放函数,或者delete操作符。
一个使用了placement语法的new表达式叫做一个placement new 表达式,使用多个参数的new 操作符或者delete操作符是一个placement new或者placement delete 函数。
void * operator new (size_t size);
void operator delete(size_t size);
void * operator new [] (size_t size);
void operator delete[] (size_t size);
- operator new/operator delete, operator new[]/operator delete[] 和malloc/free用法一样
- 他们只负责分配空间,释放空间,不会调用对象构造函数/析构函数来初始化/清理对象
- 实际operator new和operator delete只是malloc和free的一层封装
malloc/free和new/delete之间关系和差异:
(1)它们都是动态管理内存的入口
(2)malloc/free是C/C++标准库函数,delete/free是C++操作符
(3)malloc/free只是动态分配内存空间/释放内存空间,而new/delete除了分配空间还会调用构造函数与析构函数进行初始化与清理工作
(4)malloc/free需要手动计算类型大小且返回值为void*, new/delete可以自己计算类型大小并返回对应类型的指针
(5)malloc分配内存失败返回NULL指针,new分配内存失败会抛出异常
new/delete、new[]/delete[]做了什么?
显示定义析构函数:
屏蔽显示定义的析构函数后:
- 这里为ptr开辟的大小是sizeof(Type),p4是16个字节
- 只有当Type显示定义析构函数后,new Type []才会在头部多开辟空间存储数组个数,因为int为语言内置类型,所以并没有显示定义析构函数,所以不会多开辟四个字节存储数组个数
new做了两件事:
1.调用operator new分配空间
2.调用构造函数初始化对象
delete也做了两件事:
1.调用析构函数清理对象
2.调用operator delete释放空间
new[N]
1.调用operator new分配空间
2.调用N次构造函数分别初始化每个对象
delete[]
1.调用N次析构函数清理对象
2.调用operator delete释放空间
定位new表达式(replacement版本)
- 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
new (place_address) type
new (place_address) type(initializer-list)
place_address
必须是一个指针,initializer-list
是类型的初始化列表
malloc/free + 定位操作符new() 、显示调用析构函数,模拟new和delete的行为
malloc/free + 多次调用定位操作符new() 、显示调用析构函数,模拟new[]和delete[]的行为
用宏模拟实现new/delete new[]/delete[]
#define NEW_ARRAY(PTR,TYPE,N) \
do \
{ \
PTR = (TYPE*)operator new(sizeof(TYPE) * N + 4); \
(*(int*)PTR) = N; \
PTR = (TYPE*)((char*)PTR + 4 ); \
for (size_t i = 0; i < N; i++) \
{ \
new(PTR + i)TYPE(5); \
} \
}while (false);
#define DELETE_ARRAY(PTR,TYPE) \
do \
{ \
int N = *((int*)PTR - 1); \
for (int i = 0; i < N; i++) \
{ \
PTR[i].~TYPE(); \
} \
PTR = (TYPE*)((char*)PTR - 4); \
operator delete(PTR); \
}while (false);
do{
}while(false) 是为了生成一个局部域,
定义和使用临时变量,避免变量重定义