new/delete和malloc/free的区别以及联系

1.new/delete和malloc/free区别

(1)malloc/free是c语言的库函数,new/delete是C++的运算符。运算符可以重载,库函数不行。

(2)它们都能在堆上申请动态内存和释放内存。

(3)内部数据类型是编译器本来就认识的,不需要用户自己定义。如:int、char、double等等。非内部数据类型不是编译器本来就认识的,需要用户自己定义才能让编译器识别。如class myclass{…}

(4)对于非内部数据类型而言,Malloc/free无法满足动态对象的要求,因为对象在创建的同时要执行构造函数,对象在消亡的时候要执行析构函数。由于malloc/free是库函数,不在编译器的控制权限内,不能够自动实现构造函数和析构函数。

#include <iostream>

using namespace std;

class OBJECT
{
public:
	OBJECT()
	{
		cout << "OBJECT() 构造函数" << endl;
	}
	~OBJECT()
	{
		cout << "~OBJECT() 析构函数" << endl;
	}

};

void Use_malloc_free()
{
	cout << "使用malloc/free" << endl;
	OBJECT* p = (OBJECT*)malloc(sizeof(OBJECT));
	free(p);
}

void Use_new_delete()
{
	cout << "使用new/delete" << endl;
	OBJECT* p = new OBJECT;
	delete p;
}

int main()
{
	Use_new_delete(); //用new/delete创建、销毁对象
	cout << "----------------" << endl;
	Use_malloc_free(); //用malloc/free创建、销毁对象
	return 0;
}

运行结果:

使用new/delete
OBJECT() 构造函数
~OBJECT() 析构函数
----------------
使用malloc/free

可以看到malloc/free并没有自动执行对象的构造、析构函数。
简单来讲就是malloc只能分配内存,new可以分配内存,也可以构造对象;free只能释放内存,delete能够释放内存以及析构函数。
我们不能用malloc/free来完成动态对象的内存管理,应该用new/delete。由于“内部数据类型”没有析构和构造函数,这时使用两者是等价的。
注:由于C++程序经常调用C函数,所以malloc/free依然使用。

2.malloc和new的区别

(1)申请内存所在位置
new操作符是从自由存储区(free store)上为对象动态分配内存;malloc是在堆上动态分配内存。
自由存储区是C++基于new操作符的一个抽象概念;堆是操作系统上的术语,是操作系统所维护的一块特殊内存。
自由存储区可以是堆也可以是静态存储区,这都要看operator new在哪里为对象分配内存。
new还有一种变体:定位new,它能够让程序元指定要使用的位置(地址)。即new运算符提供了要使用的地址。
定位new运算符存在于头文件中。定位new只是返回传递给它的地址,并将其强制转化成void*,以便能够赋给任何指针类型。

#include <iostream>

using namespace std;

int main()
{

	char buffer[100];				//缓冲区
	cout << "buffer[100] address=" << (void*)buffer << endl;
	cout << endl;

	int* p1 = new int[10];			//常规new运算符
	for (int i = 0; i < 10; i++)
	{
		p1[i] = 10-i;
	}
	cout << "p1 address=" << p1 << endl;
	cout << "p1[0]=" << p1[0] << endl;
	cout << endl;

	int* p2;
	p2 = new(buffer) int[10];		//定位new运算符
	cout << "p2 address=" << p2 << endl;

	for (int i = 0; i < 10; i++)
	{
		p2[i] = i;
	}
	cout << "p2[0]=" << p2[0] << endl;  //在p2地址上的值,也是在buffer首地址上的值
	cout << endl;

	int* p3;
	p3 = new(buffer) int;				//定位new
	cout << "p3 address=" << p3 << endl;
	*p3 = 10;							//将buffer首地址位置的数值改为10
	cout << "*p3=" << *p3 << endl;
	cout << endl;

	int* p4;
	p4 = new(buffer + 2 * sizeof(int))int;
	cout << "p4 address=" << p4 << endl;//偏移10个int字节的新地址
	cout << endl;

	return 0;
}

运行结果

buffer[100] address=008FFC80

p1 address=00C50580
p1[0]=10

p2 address=008FFC80
p2[0]=0

p3 address=008FFC80
*p3=10

p4 address=008FFC88

p1的地址和buffer不一样,这是因为p1是新开辟的地址。
p2和buffer地址一样,这就是定位new的作用,可以看到p2的起始地址(即buffer的起始地址)的值为0
p3和buffer地址也一样,但是我们改变了数值大小,为10。
p4是相对于buffer偏移了2*4个字节,即8个字节。

关于定位new的隐患:

#include <iostream>

using namespace std;

class A
{
public:
	A()
	{
		cout << "creat!" << endl;
	}
	~A()
	{
		cout << "destory!" << endl;
	}

};
int main()
{
	char buffer[100];
	A *a = new(buffer) A;
	return 0;
}

输出

creat!

并没有调用析构函数
加上如下代码

a.~A();

输出如下

creat!
destory!

此处不能使用delete a;原因我还在想。
类在释放自己的空间前,会自动调用析构函数,但是在定位new这里,不能通过delete 语句释放对象,所以,对象就不能隐式的调用析构函数,需要在buffer释放前,显式的调用析构函数。
(2)返回类型安全性
new操作符内存分配成功后,返回的时对象类型的指针,类型严格与对象匹配,无须进行转换,所以new是符合安全类型的操作符。
malloc内存分配成功返回的是void*,需要强制转换成我们所需要的类型。

(3)内存分配失败的返回值
new内存分配失败后,会抛出bac_alloc异常,不会返回NULL;而malloc内存分配失败会返回NULL。
C语言

int *a  = (int *)malloc ( sizeof (int ));
if(NULL == a)
{
    ...
}
else 
{
    ...
}

C++语言

try
{
    int *a = new int();
}
catch (bad_alloc)
{
    ...
}

(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[])函数释放空间。
然而malloc/free并不会这样,具体上面第1条刚讲过。

(6)对数组的处理

C++提供了new[]和delete[]来专门处理数组

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

至于malloc,它并不知道你要在这块内存上放的是什么(数组还是单个整形变量),它只会给你一块原始内存以及它的地址;具体想要什么样的你自己去实现。例如:

int * ptr = (int *) malloc( sizeof(int)* 10 );//分配一个10个int元素的数组

(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[](size_t);
void * operator delete (void * )noexcept;
void * operator delete[](void *0noexcept;
//这些版本承诺不抛出异常
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;

我们可以自定义上面版本的任何一个,前提是自定义版本必须位于全局作用域或者类内部。
然而malloc/free不支持重载。

(9)能够再重新分配内存
使用malloc分配内存之后发现内存不足,可以使用realloc函数进行内存的重新分配。
realloc函数先判断当前指针是否有足够的连续空间,如果有,原地扩大内存地址,并返回原来的地址指针;如果空间不够,就重新找到满足新指定大小的内存,分配空间,再将原有数据从头到尾拷贝到新的内存区域,再释放原来的内存区域。
new没有这样的操作。

(10)客户处理内存分配不足
malloc没有这样的功能。
在operator new无法满足某一个内存分配的需求的时候,它会抛出一个异常,在这之前,它会先调用一个客户制定的错误处理函数new_handler。为了指定这个来处理内存不足的函数,使用者需要调用标准库程序函数set_new_handler:

namespace std{
    typedef void (*new_handler){ };//一个函数指针
    new_handler set_new_handler(new_handler p) throw();
};

set_new_handler实际上就是一个形参和返回值类型都是new_handler的函数。
具体如下:

void outOfMem(){
    std::cerr<<"Unable to statisfy request for memory\n";
    std::abort();
}
int main()
{
    set_new_handler(outOfMem);
    int *pBigDataArray=new int[100000000L];
    ...
}

如果不能为operator new分配所需要的100000000个整数的空间,outOfMem就会被调用,于是程序终止(abort)。如果一直不能满足的话,outOfMem也就是之前所说的new_handler函数会不断被调用。
那么如何设计一个良好的new_handler:
①让更多的内存被使用。这样做的目的很简单,就是为了内存能够尽可能被operator new进行分配。实现此要求的做法是,当new_handler被调用的时候,将之前分配的内存释放给程序,以便能够继续使用。
②安装新的new_handler。如果当前的new_handler无法取得更多的内存,那么这个new_handler应该能够安装新的new_handler以便能够获得更多的内存空间。
③卸载new_handler,也就是将null指针传给set_new_handler。这样的话,一旦没有分配成功,就会抛出异常。
④抛出bad_alloc的异常。这样的异常不会被operator_new捕捉,因此会被传播到内存索求处。
⑤不返回。通常会调用abort或者exit。

细说new与malloc的10点区别
关于set_new_handler的理解

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值