深⼊理解C语⾔的new[]和delete[]
⽬录
1、重载操作符
2、new和delete的原理
3、new[]和delete[]的原理
总结
c++的动态内存管理⽅式和c语⾔不⼀样,在c++中使⽤new和delete来替换c语⾔中的malloc和free。这⾥有⼏个点不⼀样,1、new和delete是操作符,malloc和free是函数(我的理解是c++将new和delete约定为操作符⽽已,new和delete操作符重载函数本质上还是函数)
2、c++有了类的概念,类对象的初始化除了要分配内存,还需要对内存进⾏初始化!所以,c++必须引⼊⼀种新的内存分配⽅式,既可以像malloc⼀样开辟内存,还要能够调⽤类对象的构造函数(delete的引⼊同理)。
3、new[]和delete[]是c++完全新增的内存操作符,他们和new和delete也是有不⼀样的地⽅。
下⾯,咱们来⼀⼀讲解⬇
1、重载操作符
既然new和delete都是操作符,咱们可以对new和delete进⾏重载;当你再使⽤new和delete操作内存时,编译器就会调⽤到咱们⾃⼰重载的new/delete全局函数了。(如对操作符重载不了解的,请⾃⾏补充知识)
void* operator new(size_t size){
if(size == 0) size = 1;
void* ptr = malloc(size);
if(ptr == nullptr){
std::cout << "ERROR NEW!" << std::endl;
}
std::cout << "NEW Memory Size = " << size << " address = " << ptr << std::endl;
return ptr;
}
void* operator new[](size_t size){
if(size == 0) size = 1;
void* ptr = malloc(size);
if(ptr == nullptr){
std::cout << "ERROR NEW[]!" << std::endl;
}
std::cout << "NEW[] Memory Size = " << size << " address = " << ptr << std::endl;
return ptr;
}
void operator delete(void* ptr){
std::cout << "DELETE " << ptr << std::endl;
if(ptr) free(ptr);
}
void operator delete[](void* ptr){
std::cout << "DELETE[] " << ptr << std::endl;
if(ptr) free(ptr);
}
此时,再使⽤ int* p = new int {1}; 开辟内存,那么,c++编译器会⾃动链接到我们刚才的操作符重载函数 void*
operator new(size_t size) ,⾄于编译器是怎么将 int* p = new int {1}; 解析成 void* operator new(size_t size) 函数
的,咱们不关⼼,咱们只要知道编译器做了这样⼀层代码解析转换即可。并且,转换的过程中会将需要开辟的内存⼤⼩当作形式参数传递过去!
这⾥还有⼀点需要注意:函数的返回值是void*,⽽咱们new操作接收的返回值为int*,这⾥其实也是编译在底层做了⼀层转换!(⾮常重要,new 的返回值和 operator new 函数的返回并不完全相同,包括类型和地址)
2、new和delete的原理
在开始验证new和delete之前,咱们先使⽤以下代码:
#include <iostream>
#include <stdlib.h>
#include <memory>
using namespace std;
//此处添加前⾯⼀步重载new和delete的代码
printf怎么加endl
class Master
{
public:
Master()
{
printf("constructor ---- %p\n",this);
c = rand()%10000;
}
~Master()
{
printf("destructor ---- %p\n", this);
}
public:
int c;
};
int main()
{
Master * p = new Master{};
delete p;
return 0;
}
推荐⼀个在线调试cpp的⽹站:Compiler Explorer ()
Master * p = new Master{}; 这⼀⾏的汇编信息如下:
这⾥有3个点要注意,
内存⼤⼩在编译时确定
会先调⽤operator new(unsigned long)函数
最后会调⽤构造函数
那么,delete操作的汇编信息应该和new刚好相反,这个可以⾃⾏测试!
下⾯将咱们重载的operator⽅法添加进去,然后执⾏,output⽇志信息如下:
NEW Memory Size = 4 address = 0x2154eb0
constructor ---- 0x2154eb0
destructor ---- 0x2154eb0
DELETE 0x2154eb0
通过⽇志,咱们也能够得出这样的结论:
new:先malloc内存,然后执⾏构造函数 delete:先执⾏析构函数,然后free内存
3、new[]和delete[]的原理
前⾯咱们说了new/delete与new[]/delete[]有⼀些不同,那我们先看看⽇志信息,请添加以下测试代码:int main()
{
Master * p = new Master{};
//测试new[]
Master * pArray = new Master[5];
printf("pArray = %p\n", pArray);
printf("pArray[0] = %p\n", &pArray[0]);
printf("pArray[1] = %p\n", &pArray[1]);
// delete[] pArray;
// delete p;
return 0;
}
///
output:---------------------------------------begin
NEW Memory Size = 4 address = 0x565422925eb0
constructor ---- 0x565422925eb0
NEW[] Memory Size = 28 address = 0x5654229262e0
constructor ---- 0x5654229262e8
constructor ---- 0x5654229262ec
constructor ---- 0x5654229262f0
constructor ---- 0x5654229262f4
constructor ---- 0x5654229262f8
pArray = 0x5654229262e8
pArray[0] = 0x5654229262e8
pArray[1] = 0x5654229262ec
output:----------------------------------------end
重点来了,malloc返回的内存地址(0x5654229262e0)和new[]返回的地址(0x5654229262e8)并不⼀致,⽽且申请的内存⼤⼩并不是Master类的⼤⼩ 4*5=20,⽽是28!!
通过汇编代码,我们看到这⾥确实偏移了8个字节!其他的操作与new没有多⼤差别,都是先调⽤operator new[],然后再循环调⽤数组中的每个对象的构造函数!
通过output⽇志和汇编分析,可以得到我们前⾯讲到的结论:(⾮常重要,new 的返回值和 operator new 函数的
返回并不完全相同,包括类型和地址)!
我们调⽤ delete[] pArray; 删除的地址为 0x5654229262e8,⽽真实触发 operator delete[] 的形参为 0x5654229262e0,详细⽇志信息如下:
也就是operator new[] 和 operator delete[] 的操作的内存是真实地址,并且是正确的!为什么会这样?就是因为c++编译器在底层做了⼀次转换!
这个问题也很好理解,如果编译器没有这样⼀层逻辑转换,当你 delete[] ⼀个数组时,编译器要怎么操作呢?他怎么知道数组的长度然后遍历去调⽤析构函数呢?所以,这样⼀层逻辑转换有了存在的意义,这也体现在 new[] 数组时为什么⼤⼩并不是 =对象⼤⼩*数组长度,⽽是多了8个字节!
好了嘛,那多出来的8字节是不是就是数组长度呢?咱们可以把这块内存信息输出!添加以下代码:
unsigned long* pSize = (unsigned long*)pArray - 1;//偏移8字节
printf("p = %p\n", pSize);
printf("sizeof pArray = %d\n", *pSize);//输出该地址上的内存数据
⽇志输出:
偏移8字节之后就是咱们 operator new[] 函数分配的真实的内存地址,然后咱们输出该地址8字节空间的数据信息为5,这就是申请的数据空间的⼤⼩!
也就是有了这个多余的8字节(在32位系统上为4字节),new[] 存储数组长度到该内存, delete[] 时就能够从该内存读取数组长度,并遍历数组去调⽤析构函数啦!
总结
本篇⽂章就到这⾥了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论