malloc/free 是C语言中内存申请和释放函数,利用他们我们可方便的管理内存,而在C++中我们又有了新的工具new/delete。我想你可能会迷惑:有了malloc/free为什么还要new/delete呢?malloc/free与new/delete到底有什么区别,我们看下面的代码:
// Obj.h 文件 声明CObj类
#include <iostream>
class CObj
{
public:
CObj(void);
virtual ~CObj(void);
void Hello();
};
CObj::CObj(void)
{
cout << "CObj is born!" << endl;
}
// Obj.h 文件 实现CObj类
CObj::~CObj(void)
{
cout << "CObj is died!" << endl;
}
void CObj::Hello()
{
cout << "Hello I'm the CObj!" << endl;
}
// main.cpp 测试程序
int _tmain(int argc, char* argv[])
{
cout << "Using malloc 和 Free....." << endl;
CObj *pObjA = (CObj *)malloc(sizeof(CObj));
pObjA->Hello();
free(pObjA);
cout << endl;
cout << "Using new 和delete....." << endl;
CObj *pObjB = new CObj;
pObjB->Hello();
delete pObjB;
}
上述代码段的执行结果是:
Using malloc 和 Free.....
Hello I'm the CObj!
Using new 和 delete.....
CObj is born!
Hello I'm the CObj!
CObj is died!
通过结果我们可以知道:new/delete在管理内存的同时会调用类的构造函数和析构函数。而malloc/free仅仅实现了内存的分配和释放,没有调用类的构造函数和析构函数。
malloc/free是C/C++语言的标准库函数,而new/delete是C++语言的运算符关键字。由于malloc/free是C/C++语言的标准库函数,所以在使用时需要头文件库函数支持。对于非内置数据类型,用malloc/free无法完成动态对象的创建要求。这是因为对象的创建时会自动的调用构造函数,释放时会调用析构函数。由于malloc/free不是运算符,不受编译器控制。所以无法完成对象创建和释放时构造函数和析构函数的调用。所以我们可以打这么一个比方:通过new创建一个对象,相当于盖一座房子,而malloc相当于申请一块地皮,要想成为房子还需要很多的努力。
void *malloc(long NumBytes);
该函数分配NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。关于分配失败的原因,应该有多种,比如说空间不足就是一种。
void free(void *FirstByte);
该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
小心陷阱
- 申请了内存空间后,必须检查是否分配成功;当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。
- malloc/free应该是配对的。如果申请后不释放就会内存泄露;释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
- 虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。
new/delete为运算符,使用语法如下:
指针名 = new 类型(参数); // 单个对象内存申请
指针名 = new 类型[个数]; // 多个对象内存申请
delete 指针名; // 释放单个对象内存
delete[] 指针名; // 释放多个对象内存
小心陷阱
- new/delete应该配对使用。如果用new申请了内存,在不使用时记得通过delete将申请的内存归还系统。
- new申请内存时,返回的类型就是需要的数据类型。不需要像malloc那样进行一次强制转换。
通过上述的分析,我们可对new/delete和malloc/free区别总结如下:
- new/delete为运算符,而malloc/free为C语言标准库函数。
- 通过new创建的对象具有类型,而malloc的返回值为void*,需要进行强制类型转换。
- new在创建类型时会自动调用对象的构造函数完成对象的初始化工作,而malloc函数不会。执行delete时会自动调用对象的析构函数,而free则不会。
- new申请内存失败时,会调用new_handler处理函数。而malloc申请内存失败时仅仅返回NULL。不会进行任何的善后处理。
下面我们主要讨论一下new/delete,众所周知new运算符可完成内存的申请和构造函数的初始化调用。如你认为这是new的全部,那你就错了。我不得不说,你被new伪装的外部迷惑了。的确作为new操作符时,new确实表现出了上述行为。其实new有三种不同的形态。他们是new operator、oprerator new和placement new。上述我们所讨论的只是其中一种形态:new operator。下面我们继续讨论其他两种,并给出他们的对比。
new operator的执行过程可分为三步,如图3-1所示:
(1)通过operator new 申请内存;
(2)使用placement new 调用构造函数(简单类型忽略此步);
(3)返回内存指针。如图3-1所示。可以看出new operator与其他两种形态有着密切的关系。

operator new在C++中有着与加减乘除一样的性质,C++库有标准的默认实现形式。operator new的声明如下:
void *operator new(size_t size);
operator new在默认情况下首先调用malloc实现内存的分配,如果分配成功则直接返回,如果分配失败则调用new_handler,然后重复前面的过程,直到抛出异常为止。
operator new函数的返回值为void *。值得说明的是此函数返回的是一个未经处理的指针。如果你对默认的operator new函数不是很满意,你可通过重载operator new实现满足要求的operator new函数。
operator new函数的重载,如下例所示:
class CObj
{
public:
CObj(void);
virtual ~CObj(void);
void *operator new(size_t size);
};
void *CObj::operator new(size_t size)
{
cout << "this is my new ....." << endl;
void *p = NULL;
while ((p = malloc(size)) == 0)
{
static const std::bad_alloc nomem; // report no memory
_RAISE(nomem);
}
return (p);
}
小心陷阱 重载operator函数时,需注意
- 如果你的operator new函数调用了全局的operator new函数时,需保持高度警惕,因为全局的operator new函数亦是可重载的。
- 像new/delete操作符一一对应一样,operator new 和operatordelete也是一一对应的。如果重载了operator new,那么也需重载对应的operator delete。
最后,我们介绍new的第三种形态placement new。placement new可实现对象的定位构造。通过它可以实现类对象的指定构造函数的调用。具体使用可参考下例。
#include "Obj.h"
#include <new>
using namespace std;
int main()
{
void *p = ::operator new (sizeof(CObj)); // 申请CObj所需的内存空间,但不进行初始化。
CObj *pObj = static_cast<CObj *>(p);
new(pObj) CObj(); // 调用类的构造函数,初始化分配的内存
}
placement new是标准C++库的一部分,声明在中。所以如果你使用placement new时需要包含或<new.h>。placemnet new的声明如下:
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
inline void *__CRTDECL operator new(size_t, void *_Where)
{return (_Where); }
inline void __CRTDECL operator delete(void *, void *)
{return; }
#endif
现在我们说一下new(pObj) CObj();这种奇怪的调用形式。这种调用功能:在特定的内存上调用特定的构造函数实现一个对象的构造。STL的allocator就采用placement new实现内存的管理。通过这种方式实现内存的管理具有更灵活的优点。当然,如果你通过placment new 实现对象构造函数的调用,你也必须显示的调用与之对应的placment delete:pObj->~CObj();。
通过上述的讨论我们可以得到下面的结论:
- 从功能上说,new/delete具备malloc/free的所有功能,而且远远超过malloc/free。同时还具有更好的安全特性。
- C++中保留malloc/free,目的是实现与C语言的兼容。所以作为C++的程序员,我们在编程过程中应该多使用new/delete而尽量避免使用malloc/free。
- 同样new/delete和malloc/free混合使用更是一个不明智的选择。在使用时推荐使用new/delete,尽量减少malloc/free的使用。
请谨记
- 不论new/delete还是malloc/free,使用时必须配对使用。如果将混淆了他们那将是一件蠢事。
- 不要企图用malloc/free完成动态对象的内存管理,应该使用new/delete。
- 明确区分new/delete的三种形态。

这篇博客探讨了C++中的new/delete与C语言的malloc/free的区别。new/delete不仅是内存管理工具,还能调用构造函数和析构函数,而malloc/free仅分配和释放内存。new/delete是C++的运算符,malloc/free是C语言函数。new有三种形态:new operator、operator new和placement new,分别涉及内存申请、构造函数调用和对象定位构造。建议C++程序员使用new/delete以利用其类型安全和对象管理特性,避免混合使用malloc/free。

1145

被折叠的 条评论
为什么被折叠?



