===============================================================================================================
动态内存管理
C语言的动态内存开辟和释放
C语言调用malloc,calloc,realloc函数开辟动态内存,它们都是在堆上开辟。
- malloc:从堆上分配指定大小的字节数并返回void类型,如分配失败则返回NULL,malloc分配的字节数可能比指定的字节要多,这是由内存对齐方式决定的,malloc实际上调用了HeapAlloc函数, 因此malloc分配的内存也不能跨进程调用。
-
calloc:分配指定数目的元素,每个元素的大小由size指定, 并将其初始化为0,calloc调用malloc使用C++ _set_new_mode函数来设置新的处理方式,默认情况下,malloc 失败时不调用分配内存的处理程序例程。
-
realloc:重新分配内存并返回void类型,如果没有足够的内存扩展内存块,则原来的指向的内存指针无变化,并返回NULL;如果重新分配大小设为0,而释放原来的内存块, 并返回NULL。
它们都用free
来释放。
C++的动态开辟和释放
C++通过new/delete来开辟和释放动态内存。
调用形式有:
- new(types) ( 值 ) / delete(types) 开辟一块动态内存并对其初始化。
- new (types) [ ] / delete[ ] 动态开辟一个数组, 释放时要对应起来。中括号内是数组元素的个数
new/delete操作符的调用过程:
-
new的调用过程:使用new操作符时先调用相应的 operator new(size_t) 函数,动态分配内存。如果 operator new(size_t) 不能成功获得内存,则调用 new_handler() 函数用于处理new失败问题。如果没有设置 new_handler() 函数或者 new_handler() 未能分配足够内存,则抛出 std::bad_alloc 异常。“new运算符”所调用的 operator new(size_t) 函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的 内部(成员函数)、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的 ::operator new(size_t) 函数。
构造函数
在分配到的动态内存块上 初始化 相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。 -
-
delete的调用过程:
先调用相应类型的析构函数对可能涉及动态开辟的内部资源释放,
再调用相应的 operator delete(void *) 函数。调用顺序参考上述 operator new(size_t) 函数(ADL规则)。
用#define来实现new[] / delete[]:
#define NEWARRAY(P, TYPE, N) \
do{ \
P = (TYPE*)opertor new(sizeof(TYPE)*N + 4);//多开辟4个字节 \
*((int*)P) = N;// 实际开辟出来的第一个位置存放元素个数 \
size_t i = 0; \
for (; i < N; i++) \
new(P + i)TYPE; // 开辟出来的空间一块一块初始化\
} while (false);
#define DELETEARRY(P, TYPE)\
do{ \
size_t N = 0; \
N = *((TYPE*)((char*)P - 4));// 得先获取到数组元素个数 \
P = (TYPE*)P \
for (size_t i = 0; i < N; i++)// 数组元素个数决定析构次数\
P[i].~TYPE(); \
P = (TYPE*)((char*)P - 4) \
operator delete P \
} while (false);