之前我是寡闻了,最近读书时,发现一个图表,上面的内容中发现new与delete是可以重载的,真的是出乎意料!居然还有这种操作,然后我在网上查了一下,看到几个简单的示例,作为学习方式,写下这篇博客巩固一下。
没错,new与delete都是一种操作符,当我们在代码中new一个对象时,分为两步,首先是operator new的运作,就像C语言中的malloc一样,负责分配内存,然后是placement new的运作,负责给分配到的内存初始化(当我们给某个类重载new时,就是这个placement new调用的类构造函数)。其中前面那个负责分配内存的步骤,才是我们可以重载的new步骤,至于为什么要把new、delete设计成可以重载的样子,估计是为了提高C++的灵活性吧,比如我们自己编写的new和delete,可以加入一个统计内存申请次数和内存释放次数等。
new重载的方式有两种,第一种是隐藏式重载,意思就是把本来提供给我们的new操作符版本给隐藏掉(这是我个人的猜测),这就意味着用这个方式重载new时,必须且只能给函数提供一个unsigned int型参数(size_t),因为正式的new版本有一个size_t类型参数,这个参数代表要申请的内存字节的大小;第二种方式是改变参数特征标,给operator new增加其他的参数,但是第一个参数必须得是size_t类型!
首先我们来看第一种类型的operator new重载示例:
#include <iostream>
using namespace std;
int i = 0;
void* operator new(size_t a)
{
cout << "这是非常荒唐且危险的程序!" << endl;
return &i;
}
int main()
{
cout << "i的值是: " << i << endl;
int *p = new int;
*p = 9999;
cout << "现在i的值却被意外改变了: " << i << endl;
return 0;
}
运行结果会是:
【i的值是:0】
【这是非常荒唐且危险的程序!】
【现在i的值被意外改变了:9999】
正像示例中说的那样,这样的代码很不负责任!正确的做法应该是配合库函数malloc使用,但我想为了更好的诠释operator new重载的灵活性,所以还是写了这种烂代码来做说明。但在下面的代码示例中,我会将功补过,使用一种相对正确的做法来诠释operator new的多参数重载版本:
#include <iostream>
using namespace std;
int i = 1;//用来统计我们的new使用了多少次
void* operator new(size_t a,int b)
{
cout << "这是我们第" <<i<<"次使用我们自己的new操作符"<< endl;
i++;
return malloc(a);
}
int main()
{
int *p1 = new (i)int;//调用我们的new
int *p2 = new int;//这个调用的是正式的new
int *p3 = new (i)int;//调用我们的new
return 0;
}
值得特别说明的是,在我们重载的函数中,i对应的是b,int(内容为4)对应的才是a,如果读者朋友有心,可以在函数中自己测试一下,这里不再赘述。
以上两种new的重载,都是属于全局new的重载,但事实上,我们并没有重载全局new与delete的理由,因为正式的new,要比我们自己定义的new合理的多!C++之所以允许new能够被重载,主要是应付在我们自己的类上面,比如我们自己开发了一个类,如果在堆上给这个类开辟一个类对象时需要一些特殊的动作,那么我们应该给这个类域重载一个我们的new操作符,这样当我们new这个对象时会执行那些特殊的动作,下面就是代码,为了不文不对题,也有delete的重载示例(两个重载的操作符只对本类有效,对派生类无效,如果有派生类的话):
#include <iostream>
using namespace std;
class A
{
public:
int a;
A() :a(0) {};
void * operator new(unsigned i)//new的重载示例
{
cout << "调用我们自己的new操作符" << endl;
if (i > sizeof(A))//如果调用此new的是派生类(大于A)
{
return ::operator new(i);//那么调用全局的正式new
}
return malloc(i);
}
void operator delete(void * p,size_t i)//第一个p代表要释放的首地址指针
{
cout << "调用我们自己的delete操作符" << endl;
if (p)//检测指针是否为空,以防删除空指针报错!
{
if (i != sizeof(A))
{
return ::operator delete(p);
}
free(p);
}
}
};
int main()
{
A * p(new (A));
delete p;
return 0;
}