C/C++动态内存管理

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++通过newdelete动态管理内存
  • 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) 是为了生成一个局部域,
定义和使用临时变量,避免变量重定义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值