8. 了解各种不同意义的new 和 delete
C++中关于new 的形态主要有三种:new operator, operator new, placement new
三者的用法有着不同,注意区别适用的条件:
new operator:
new 操作符,和C++语言其它操作符(如+, -, ->, ::....)一样,是由语言内建的,总是完成相同的事情,程序员不能改变其意义。如下为new的一种用法:
string *ps = new string("hazirguo");它主要完成的任务包括三个方面:
- 分配足够的内存,用来放置某类型对象。上例中分配足够放置一个string对象的内存。
- 调用一个constructor,为分配的内存的对象初始化。上例中就调用string类的构造函数string::string( const string &s );
- 返回相应的指针。上例中返回指向新建string对象的指针。
注:new 和 sizeof 一样,是C++语言的关键字,不是函数,不能够被重载!
operator new:
上面new操作符完成的任务的第一点是调用函数执行分配内存动作,类似于C语言中malloc()函数的功能,你可以重载或重写此函数,改变其行为,这就是operator new 这个函数完成的功能。通常声明为:
void * operator new (size_t size);参数size表示需要分配的内存的大小;函数的返回值为void *, 指向一块原始的、未初始化的内存。
该函数可以被重载,但第一参数的类型必须总是size_t。全局的operator new 的重载形式有多种,你也可以根据自己的需要写出自己的重载函数,如:
int *pnum = new int(5); //使用new operator//下面自己写一个operator new重载形式,初始化分配的内存
void *operator new (size_t size, int n){void *tmp = ::operator new(size); //使用全局operator new函数的一种重载形式分配内存*static_cast<int *>(tmp) = n;return tmp;
}//调用自己版本的重载函数
int *pnum = static_cast<int *>operator new(sizeof(int), 5);
placement new:
placement new 是operator new 下面的一个重载版本:
void * operator new (size_t size, void * location){return loaction;
}完成在一个已经分配好的原始内存上构建一个新的对象并初始化。因为在已经分配好的内存空间上调用constructor进行对象初始化无意义,此时就用到placement new。使用前要加上头文件#include <new>,使用的方法如下:
CTest* pT = new(p) CTest(...); //在p指向的内存中调用CTest的构造函数来初始化CTest对象,并返回此对象的指针。
举个实际的例子:char *buffer = new char[sizeof("hazirguo")+1]; //事先分配内存string *pch = new(buffer) string("hazirguo"); //完成对象的初始化
小结:
1. 如果希望将对象产生与堆上,使用new operator, 不但完成内存分配而且为该对象调用一个constructor。
2. 如果只打算分配内存,而不希望任何constructor被调用,请调用new operator函数。
3. 如果打算在已分配的内存中构造对象,请使用placement new。
同理,对于delete也有类似的用法:
delete operator:
对应与new operator的释放动作,既能够析构指针所指的对象,又能够释放被该对象占用的内存。如:
string *ps = new string("hazirguo");...delete ps; //使用的是delete operator//上句完成的功能近似于下面代码完成的功能:
ps->~string(); //1.调用对象的dtoroperator delete(ps); //2.释放对象所占用的内存
operator delete:
完成内存释放动作的函数,通常声明如下:
void operator delete(void *memoryToBeDeallocated);如果只打算处理原始的、未初始化的内存,应该完全回避new operator 和 delete operator,改调用operator new 取得内存并以operator delete归还给系统:void *buffer = operator new (50 * sizeof(char)); //分配放置50个char型的内存空间,没有任何ctor...operator delete (buffer); //释放分配的内存,没有任何dtor完成的功能类似与C语言中的malloc 和 free 函数的功能。
placement delete:
如果是使用placement new, 在某内存块中产生对象,应该避免对那块内存使用delete operator。因为delete operator会调用operator delete来释放内存,但该内存内含的对象最初并非是由operator new分配而来的。毕竟placement new只是返回它所接受的指针而已,并不知道那个指针从哪里而来。所以为了抵消该对象的constructor的影响,应该直接调用该对象的destructor。
举例:
下面一个实例来源于网络,感觉很能说明问题,摘于此供理解:
/*
F1 中的 new operator,他的行为就像F2中的 operator new 和 placement new 一样,也可以用F3中的方法达到相同的效果。*/#include <new>
#include <iostream>using namespace std;class CTest
{public:
CTest(int _x, int _y){X = _x;Y = _y;}~CTest(){X = 0;Y = 0;}void Test(char* sz){printf("%s: X=%d Y=%d \n", sz, X, Y);
}int X;
int Y;
};/* new operator && delete operator */
void F1()
{CTest* pT = new CTest(1, 1); // new operatorpT->Test("F1");
delete pT; // delete operator}/* operator new + placement new && operator delete + placement delete */
void F2()
{void* p = operator new(sizeof(CTest)); // operator new : 分配内存CTest* pT = new(p) CTest(2, 2); // placement new: 构造对象pT->Test("F2");
pT->~CTest(); // 必须显示析构对象
operator delete(pT); // operator delete: 释放内存}/* also can implement like this */
void F3()
{char* p = new char[sizeof(CTest)]; // new operator: char为内置类型,不会调用构造函数,相当于只分配内存CTest* pT = new(p) CTest(3, 3); // placement new: 在这块内存上构造CTest对象pT->Test("F3");
pT->~CTest(); // 必须显示析构CTest对象
delete [] p; // delete operator: char为内置类型,不会调用析构函数,相当于只释放内存}void main()
{F1();F2();F3();}
前面都是对单对象,面对数组时,还有一些形式需要注意,如:
string *ps = new string[10]; //分配一个对象数组此时,内存分配不再是operator new,而是名为operator new[]的函数负责,但是这个函数同样可以被重载。(。。。此处待补充)
参考文献: 《More Effective C++ 35个改善编程与设计的有效方法 中文版》
邮箱:haifenglinying#yahoo.cn (#->@)
个人主页:www.hazirguo.com