详解malloc/free和new/delete

来源:互联网 发布:智能电视机网络电视机 编辑:程序博客网 时间:2024/05/17 07:11

      今天就浅谈一下malloc/free和new/delete的区别,剖析一下它们的工作原理以及使用宏来模拟实现new/delete的功能。

      有了 malloc/free 为什么还要 new/delete ?

       malloc 与 free 是 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态内存和释放内存。它们都可用于申请动态内存和释放内存。
       对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free,因此 C++ 语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete 。

区别:

①malloc/free是c/c++的标准库函数,而new/delete则是c++中的运算符
②malloc/free只是动态分配内存空间/释放空间。而new/delete除了这些以外还会调用类的构造函数和析构函数进行初始化和清理;
③malloc/free需要手动计算类型大小,且返回值为void*,new/delete则可以自己计算类型大小并且返回对应类型的指针
④申请的内存所在位置:malloc在堆上动态分配内存,new在自由存储区为对象动态分配内存;
注意从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。(关于自由存储区和堆的区别可以参考一本书exceptional C++
⑤:动态内存分配失败时的返回值:new内存分配失败时,会抛出bac_alloc异常,不会返回NULL;malloc分配内存失败时会返回NULL;


底层实现:

在使用new的时候做了两件事:
1、调用operator new分配空间
2、调用构造函数初始化对象

在使用delete的时候也做了两件事:
1、调用析构函数清理对象
2、调用operator delete函数释放空间

在使用new[N]的时候也做了两件事:
1、调用operator new分配空间
2、调用N次构造函数初始化N个对象

在使用delete[]的时候也做了两件事:
1、调用N次析构函数清理N个对象
2、调用operator delete函数释放空间

operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一样。他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。实际operator new和operator delete只是malloc和free的一层封装。

我们要思考的是在调用delete[ ]时,编译器是怎么知道要调用多少次析构函数来清理对象。

其实在使用new[ ]创建多个对象时,编译器会多开辟四个字节的空间用来存储对象的个数,因此在使用delete[ ]时编译器将会获得要清理的对象个数。(注意:当使用delete [ ]时,我们必须显示定义析构函数,否则会造成内存泄漏)

我们以一个日期类为例子:

new [ ]







delete [ ](一定要显示定义析构函数,否则可能会造成内存泄露)




为什么malloc/free、new/delete和new[]/delete[]要匹配使用?

#include<iostream>using namespace std;class Array{public:Array(size_t size = 10): _size(size), _a(0){cout << "Array(size_t size)" << endl;if (_size > 0){_a = new int[size];}}~Array()//显示的定义了析构函数{cout << "~Array()" << endl;if (_a){delete[] _a;_a = 0;_size = 0;}}private:int* _a;size_t _size;};

这里我们只看看使用的情况:(仅对自定义类型不考虑内置类型

Array* p1=new Array;
①delete p1;//程序正常
②delete [] p1;//程序崩溃,delete会在p1-4的地址处释放空间造成程序崩溃
③free(p1);//会造成内存泄漏,free不会调用析构函数

Array* p2=new Array[10];
①delete p1;//程序不仅内存泄漏(仅对第一个对象调用了析构函数),还会崩溃(释放空间的地址不对,在起始地址+4的地址处释放造成程序崩溃)
②delete [] p1;//程序正常
③free(p1);//程序崩溃,内存泄漏

注意:对于内置类型而言,由于释放空间不需要调用析构函数,因此,也就不会多开四个字节,也就是说:delete和delete[]是相同的效果(尽管如此,还是建议大家要匹配使用)。


实现NEW_ARRAY/DELETE_ARRAY宏,模拟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;}while (false);#define DELETE_ARRAY(PTR,TYPE)   \do     \{size_t N = *((int*)PTR-1);for (size_t i = 0; i < N; i++)PTR[i].~TYPE();PTR = (TYPE*)((char*)PTR - 4);      \operator delete PTR;     \}while (false);







原创粉丝点击