title: C++ new delete malloc free

date: 2020-03-10

categories: c++

tags: 语法

C++的new delete malloc free以及区别。

new delete是运算符,malloc和free是函数

1.new 和 malloc

1.1 new

int *p = new int[INT_NUM];
A* example = new A(1);

1、new operator

这个就是平时最经常用的new,用法如下程序所示:

 1 class A
2 {
3 public:
4 A(int i) :a(i){}
5 private:
6 int a;
7 };
8
9 int main()
10 {
11 A* example = new A(1);
12 }

new operator实际上执行了以下三个步骤:

调用operator new分配内存(后面要说的第二种new),如果类本身定义了operator new,那么会调用类自己的operator new,而不是全局的;

调用A的构造函数A::A(int);

返回相应的指针

2、operator new

operator new不调用构造函数,而仅仅分配内存,有两个版本,前者抛出异常,后者当失败时不抛出异常,而是直接返回:

void* operator new (std::size_t size);

void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;

可以看到,operator new的作用有点类似与C语言中的malloc,有的地方说operator new的底层实现可以是malloc。

C++中可以用set_new_handler()设置抛出bad_alloc()异常时调用的处理函数

3、placement new

3、placement new仅在一个已经分配好的内存指针上调用构造函数,基本形式如下:

void* operator new (std::size_t size, void* ptr) noexcept;

placement new不需要抛出异常,因为它自身不分配内存。

同时,ptr指针是已经分配好的,可能在栈上也可能在堆上,如下面的例子:

 1 class A
2 {
3 public:
4 A(int i) :a(i){}
5 int getValue(){ return a; }
6 private:
7 int a;
8 };
9
10 int main()
11 {
12 A* p1 = new A(1); //在堆上分配
13 A A2(2); //在栈上分配
14 A* p2 = &A2;
15 cout << "placement new前的值: " << p1->getValue() << " " << p2->getValue() << endl;
16
17 A* p3 = new(p1) A(3); //在p1的位置上构造
18 A* p4 = new(p2) A(4); //在p2的位置上构造

1.2 malloc free

malloc()函数的头文件是stdlib.h,其函数声明如下:

 void* malloc(size_t size);

其中参数size_t size表示动态内存分配空间的大小,以字节为单位。

在这里malloc()函数的返回值是一个指针,或者说是分配后内存空间的首地址

    void  free(void *ptr)

    int *p = NULL;
    p = (int *)malloc(sizeof(int));//开辟一个int类型大小的空间,用p指针指向它
    *p = 10;//将10赋值给p指针指向的空间
    cout << *p << endl;//输出:10
    free(p);//释放p指针指向的空间

1.3 delete

在C++中delete函数用于回收new分配的内存空间。

C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。

关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。

在针对简单的基本数据类型,使用delete也可以回收 new[] 分配的一组对象的内存空间,因为:基本的数据类型对象没有析构函数,并且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。

但是在对于自定义类型的时候,使用new[] 分配的一组对象的内存空间最好还是要用delete[]回收。

T* p1 = new T[NUM];
cout << p1 << endl; //输出P1的地址
// delete[] p1;
delete p1;

1.4 new malloc的区别

堆上开辟int整形:
int *p1 = (int*)malloc(sizeof(int)); 根据传入字节数开辟内存,没有初始化
int *p2 = new int(0); 根据指定类型int开辟一个整形内存,初始化为0
int *p3 = (int*)malloc(sizeof(int)*100); 开辟400个字节的内存,相当于包含100个整形元素的数组,没有初始化
int *p4 = new int[100](); 开辟400个字节的内存,100个元素的整形数组,元素都初始化为0

malloc开辟内存只有一种方式,而new有四种分别是普通的new(内存开辟失败抛出bad_alloc异常), nothrow版本的new,const new以及定位(placement)new。

以下参考https://www.cnblogs.com/shilinnpu/p/8945637.html

  1. 申请的内存所在位置

    new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。

那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。

默认是在堆上。

特别的,new甚至可以不为对象分配内存!定位new的功能可以办到这一点:

new (place_address) type

place_address为一个指针,代表一块内存的地址。当使用上面这种仅以一个地址调用new操作符时,new操作符调用特殊的operator new,也就是下面这个版本:

void * operator new (size_t,void *) //不允许重定义这个版本的operator new

这个operator new不分配任何的内存,它只是简单地返回指针实参,然后右new表达式负责在place_address指定的地址进行对象的初始化工作。

2.返回类型安全性

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图方法自己没被授权的内存区域。关于C++的类型安全性可说的又有很多了。

3.内存分配失败时的返回值

new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。

在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功:

int *a  = (int *)malloc ( sizeof (int ));
if(NULL == a)

new做法应该是使用异常机制:

try
{
int *a = new int();
}
catch (bad_alloc)
{
...
} 或者C++中可以用set_new_handler()设置抛出bad_alloc()异常时调用的处理函数.
见10. 客户处理内存分配不足

4.是否需要指定内存大小

使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。

class A{...}
A * ptr = new A;
A * ptr = (A *)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A);

5.是否调用构造函数/析构函数

使用new操作符来分配对象内存时会经历三个步骤:

第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。

第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。

第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

第一步:调用对象的析构函数。

第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。

总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。

6.对数组的处理

C++提供了new[]与delete[]来专门处理数组类型:

A * ptr = new A[10];//分配10个A对象

使用new[]分配的内存必须使用delete[]进行释放:

delete [] ptr;

new对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏。

至于malloc,它并知道你在这块内存上要放的数组还是啥别的东西,反正它就给你一块原始的内存,还需要我们手动自定数组的大小。

7.new与malloc是否可以相互调用

operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。

void * operator new (sieze_t size)
{
if(void * mem = malloc(size)
return mem;
else
throw bad_alloc();
}
void operator delete(void *mem) noexcept
{
free(mem);
}

8.是否可以被重载

opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本:

//这些版本可能抛出异常

void * operator new(size_t);

void * operator new;

void * operator delete (void * )noexcept;

void * operator delete[](void *0)noexcept;

//这些版本承诺不抛出异常

void * operator new(size_t ,nothrow_t&) noexcept;

void * operator new[](size_t, nothrow_t& );

void * operator delete (void *,nothrow_t& )noexcept;

void * operator delete[](void *0,nothrow_t& )noexcept;

我们可以自定义上面函数版本中的任意一个,前提是自定义版本必须位于全局作用域或者类作用域中。太细节的东西不在这里讲述,总之,我们知道我们有足够的自由去重载operator new /operator delete ,以决定我们的new与delete如何为对象分配内存,如何回收对象。

而malloc/free并不允许重载。

  1. 能够直观地重新分配内存

    使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。

new没有这样直观的配套设施来扩充内存。

  1. 客户处理内存分配不足

    在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new-handler。new_handler是一个指针类型:

namespace std

{

typedef void (*new_handler)();

}

指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明于的一个标准库函数:

namespace std

{

new_handler set_new_handler(new_handler p ) throw();

}

set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时该调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行(但马上就要发生替换)的那个new_handler函数。

对于malloc,客户并不能够去编程决定内存不足以分配时要干什么事,只能看着malloc返回NULL。

最新文章

  1. 在应用程序级别之外使用注册为 allowDefinition=&#39;MachineToApplication&#39; 的节是错误的
  2. 一个好用的Log管理类
  3. [SQL]当输入表达式得数为一个有效的整数、浮点数、money 或 decimal 类型,那么 ISNUMERIC 返回 1;否则返回 0
  4. libserialport: cross-platform library for accessing serial ports
  5. 【转】MAC使用adb工具
  6. oracle数据库存储过程中NO_DATA_FOUND不起作用?
  7. VC++中的头文件包含问题
  8. html音视频标签
  9. python 字典操作方法详解
  10. Linux使用IDEA配置maven的web项目骨架archetype(模板) 自定义骨架
  11. 编写一个简单的基于jmespath 的prometheus exporter
  12. android shape 怎么在底部画横线
  13. Linux学习期中总结
  14. wget安装pip和pip3
  15. PHP 注册错误和异常处理机制
  16. JAVA 本地序列化。
  17. UE4添加植被Foliage Type
  18. jenkins shell脚本自动化构建阿里云k8s上应用
  19. URL地址重写例子(Helicon)
  20. 关于putty连接百度云linux服务器那些事

热门文章

  1. egret 解决游戏loading前的黑屏
  2. 与图论的邂逅06:dfs找环
  3. MYSQL(将数据加载到表中)
  4. file转化为binary对象发送给后台
  5. 手动添加Ini4idea,解决pycharm无法打开ini文件
  6. [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本
  7. (06)-Python3之--判断、循环
  8. Monkey patching
  9. status http status code 状态码
  10. 从一片森林(JavaScript)到另一片森林(C++)