最近研究运算符重载,突然了解到new和delete也是C++运算符(惭愧),以前只知道是关键字.偶有一点心得记于博客.
首先,new和malloc一样,都是C++申请动态内存的操作,但是new除了申请内存,还要对内存进行初始化,就是我们所说的调用构造函数.
其次,new申请内存失败的时候,会抛出std::bad_alloc异常, 而malloc则会返回空指针,但是如果使用new(std::nothrow)T(X1,X2...)形式,则不会抛出异常,申请失败则会返回空指针
再次,new底层的操作其实是调用malloc,在malloc返回空的时候,抛出异常
最后,来说说new的原理,
int *i= new int(3);
上面这个表达式很简单,就是new一块int型的内存,然而这里面却包含2步操作:
1.首先,调用全局的operator new(size_t size)申请一块长度为sizeof(int)的内存,
2.然后,对这块内存进行初始化,就是调用int的构造函数,把这块内存值初始化为3
根据以上2步,其实该表达式完全可以用以下表达式进行表达:
int* i = (int*)operator new(sizeof(int));
new(i)int(3);
?,充满了疑问,是不是没见过上面2种形式的表达式,其实很简单,上面2种表达式就是c++标准库的operator new的2种形式,第一种格式为void* operator new (size_t size), 第2种形式为void* operator new(size_t size, void* _Where),这2个是函数,可以直接进行调用,第一个表达式就是申请内存,第二个表达式就是返回申请到内存的指针,调用构造函数进行初始化
T*i = new T[N];
上述表达式是new[]的形式,同new也包含2步操作,
1.调用operator new(size_t size)进行申请内存的操作,但是这里的size长度不一定是sizeof(T)*N,但是T是基本类型(如int)时候,size的值为sizeof(int)*N,但是如果T是自定义长度就>sizeof(T)*N,我在VS2008测试,得出size的长度为sizeof(T)*N+4.我猜测是C++需要一部分内存对该数组进行管理
2.调用T的构造函数T()对数组里的每个对象都进行初始化
但是该表达式却不能等价于以下表达式:
T* i = (T*)operator new(sizeof(T)*N);
new(i)T;
首先这里申请的内存大小为sizeof(T)*N(因为自己管理该内存,不需要C++去管理,所以N个数组所占的内存就是这么多),其次new(i)T只是对数组的第一个对象进行初始化,可以使用
for (int j = 0; j < N; j++)
{
new(i+j*sizeof(T))T;
}
分别进行初始化
下面说说operator new的重载,
operator new 重载为成员函数的格式为 <static>void* operator new(size_t size,X1,X2...);其中要求第一个参数必须是size_t, 返回值必须是void*类型.而且static可带可不带,因为不带static也会隐式的转为静态函数,因为operator new是在构造函数前进行操作的如下例
class TestClass
{
public:
TestClass(void);
~TestClass(void);
public:
/*friend*/ new_handler set_new_handler(new_handler p);
void* operator new (size_t size)....................1
{
return malloc(size);
}
void* operator new (size_t size, int j, int v)...........................2
{
return malloc(size);
}
void* operator new (size_t size, void* p)........................3
{
return p;
}
void* operator new[] (size_t size)
{
return malloc(size);
}
void operator delete (void* p)
{
free(p);
}
void operator delete[] (void* p)
{
free(p);
}
};
调用重载new的格式为new(X1,X2...)T,如上类,当你使用TestClass *p = new TestClass;将会先调用1,在调用构造函数,使用TestClass *p = new(3,5) TestClass;将会先调用2在调用构造函数,如果使用void* p = operator new(sizeof(TestClass))将会调用C++标准库的operator new(size_t size),若要使用重载成员则为
void* p = TestClass::operator new(sizeof(TestClass)),同理new[]重载形式为 <static>void* operator new[](size_t size,X1,X2...);
delete重载形式为<static>void operator delete(void*p,X1,X2...);返回值必须为void,第一个参数为void*
delete[]重载形式为<static>void operator delete[](void*p,X1,X2...);返回值必须为void,第一个参数为void*
如果重载为友元函数,其实就是全局函数,则格式不变只是在前面添加一个friend 如friend void* operator new(size_t size,X1,X2...);但是有个比较特殊的是
void* operator new(size_t size, void* p )不能重载为友元即全局的
最后,如果你重载了new函数,也要重载对应的 delete
其实new和delete还有很多东西, 如 new在抛异常前线调用的错误处理函数,new_handler set_new_handler(new_handler p);也是可以重载的(如上类),new的异常处理等等,今天没时间写了,有心的人可以自己找点资料,这里只做简单的介绍