首先要知道new和delete是一种运算符,而malloc和free是一种系统调用函数,因此new和delete是可以重载的。
先来举个例子看一下new与delete 和 malloc与free的区别:
class A{
public:
A(){
cout << "constuctor" << endl;
}
~A(){
cout << "distuctor" << endl;
}
};
int main()
{
A *a = new A;
delete a;
cout << "--------------------" << endl;
A *b = (A*)malloc(sizeof(A));
free(b);
return 0;
}
很明显的可以看出new和delete会分别自动调用构造器和析构器,而malloc和free则不会。
一、new 和delete重载
new和delete既可以作为全局函数重载,也可以作为局部成员函数重载。先看全局函数重载:
class A{
public:
A(){
cout << "constuctor" << endl;
}
~A(){
cout << "distuctor" << endl;
}
int data;
};
void* operator new(size_t size){
cout << "size = " << size << endl;
cout << "void* operator new(size_t size)" << endl;
void *p = malloc(size);
return p;
}
void operator delete(void* p){
cout << "void operator delete(A* p)" << endl;
free(p);
}
int main()
{
A *a = new A;
delete a;
return 0;
}
输出结果:
首先在A类中加入了一个int型的数据成员,那么sizeof(A) = 4, 这里对new的重载函数的参数中有个size_t类型,这实际是个unsigned int类型, size就是用来表示需要分配的空间大小,内部的机制会知道应该分配4个字节的空间给类A的对象,至于是如何知道的,这个就对我们隐蔽了(所以对new的重载并不是真正的重载,内部的东西我们还是不知道的),通过对new和delete的重载也可以发现其实函数体中就是利用malloc和free去实现的。
现在加入一个double型的指针:
int main()
{
A *a = new A;
delete a;
double *p = new double;
delete p;
return 0;
}
结果是这样的:
可以看到就算new一个double也是用的我们重载的new和delete,这就是全局函数重载的覆盖性,特别的广,只要用到new和delete全是从全局函数去操作,因为影响范围太广,所以一般不建议这么做,那么就要进行局部成员函数重载。像这样:
class A{
public:
A(){
cout << "constuctor" << endl;
}
~A(){
cout << "distuctor" << endl;
}
void* operator new(size_t size){
cout << "size = " << size << endl;
cout << "void* operator new(size_t size)" << endl;
void *p = malloc(size);
return p;
}
void operator delete(void* p){
cout << "void operator delete(A* p)" << endl;
free(p);
}
int data;
};
int main()
{
A *a = new A;
delete a;
double *p = new double;
delete p;
return 0;
}
把重载函数放到类中,当成成员函数,运行结果就是这样的:
可以看到这样就不会影响到其他类型的new和delete了。
一、new[] 和delete[]重载
new[]和delete[]与new和delete的重载很相似,只不过多了个[]而已,这里直接用局部的,全局的就不再举例了:
class A{
public:
A(){
cout << "constuctor" << endl;
}
~A(){
cout << "distuctor" << endl;
}
void* operator new(size_t size){
cout << "size = " << size << endl;
cout << "void* operator new(size_t size)" << endl;
void *p = malloc(size);
return p;
}
void operator delete(void* p){
cout << "void operator delete(A* p)" << endl;
free(p);
}
void* operator new[](size_t size){
cout << "size = " << size << endl;
cout << "void* operator new(size_t size)" << endl;
void *p = malloc(size);
return p;
}
void operator delete[](void* p){
cout << "void operator delete(A* p)" << endl;
free(p);
}
int data;
};
int main()
{
A *a = new A[5];
delete []a;
double *p = new double[5];
delete []p;
return 0;
}
运行结果:
可以看到构造了5次,也析构了五次,重点要说的是为什么这里的size = 24, 却不是20呢。这还得从malloc说起:
char *p = (char*)malloc(100);
free(p);
这里为p开辟了100字节的空间,然后释放,只通过free(p)系统怎么知道应该释放多少空间呢,其实底层,在为p开辟空间的时候既存储了p,也存储了100这是值,那么在释放的时候就知道对p释放100空间了。new[]这个地方也是一样的,不仅存储了本身开辟的空间,同时也存储了一个指针,所以size = (4 * 5) + 4 = 24字节。
三、new重载的优点
new重载有个特别有优势的地方,我们知道类的数据成员一般都是最先在构造器中初始化的,用new重载之后,可以把数据成员的舒适化放在new中,比构造器中的初始化还早,还是使用上面的例子,这里只单独给出new的重载:
void* operator new(size_t size){
cout << "size = " << size << endl;
cout << "void* operator new(size_t size)" << endl;
void *p = malloc(size);
memset(p, 0, size);
((A*)p)->data = 100; //数据成员的初始化
return p;
}
int main()
{
A *a = new A;
cout << a->data << endl;
delete a;
return 0;
}
结果是这样的
可以看到数据成员的的确确被初始化了。
四、总结
最后再总结一下重载格式:
void* operator new(size_t size);
void* operator new[](size_t size);
void operator delete(类名* 对象名);
void operator delete[](类名* 对象名);