内存管理运算符 new、new[]、delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数。一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内存时才会重载。
以成员函数的形式重载 new 运算符:
void * className::operator new( size_t size ){ //TODO: }
以全局函数的形式重载 new 运算符:
void * operator new( size_t size ){ //TODO: }
两种重载形式的返回值相同,都是void *
类型,并且都有一个参数,为size_t
类型。
在重载 new 或 new[] 时,无论是作为成员函数还是作为全局函数,它的第一个参数必须是 size_t 类型。size_t 表示的是要分配空间的大小,对于 new[] 的重载函数而言,size_t 则表示所需要分配的所有空间的总和。
重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。
我们首先需要明白的一点就是:operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。
size_t 在头文件 <cstdio> 中被定义为typedef unsigned int size_t;
,也就是无符号整型。
当然,重载函数也可以有其他参数,但都必须有默认值,并且第一个参数的类型必须是 size_t。
同样的,delete 运算符也有两种重载形式。以类的成员函数的形式进行重载:
void className::operator delete( void *ptr){ //TODO: }
以全局函数的形式进行重载:
void operator delete( void *ptr){ //TODO: }
两种重载形式的返回值都是 void 类型,并且都必须有一个 void 类型的指针作为参数,该指针指向需要释放的内存空间。
当我们以类的成员函数的形式重载了new 和 delete 操作符,其使用方法如下:
如果类中没有定义 new 和 delete 的重载函数,那么会自动调用内建的 new 和 delete 运算符。
重载全局new和delete
#include <QCoreApplication> #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define inf 0x7fffffff using namespace std; void* operator new(size_t sz) { printf("operator new: %d Bytes\n",sz); void* m = malloc(sz); if (!m) puts("out of memory"); return m; } void* operator new [](size_t sz) { printf("operator new: %d Bytes\n",sz); void* m = malloc(sz); if (!m) puts("out of memory"); return m; } void operator delete(void* m) { puts("operator delete"); free(m); } void operator delete[](void* m) { puts("operator delete"); free(m); } class S { public: S() {puts("S::S()"); } ~S() {puts("S::~S()"); } private: int an[1000]; }; void func() { puts("creating & destroying an int"); int* q = new int(23); delete q; puts("creating & destroying an int[]"); int* p = new int[10](); delete []p; puts("creating & destroying an s"); S* s = new S; delete s; puts("creating & destroying S[3]"); S* sa = new S[3]; delete []sa; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); func(); return a.exec(); }
输出:
creating & destroying an int operator new: 4 Bytes operator delete creating & destroying an int[] operator new: 40 Bytes operator delete creating & destroying an s operator new: 4000 Bytes S::S() S::~S() operator delete creating & destroying S[3] operator new: 12004 Bytes S::S() S::S() S::S() S::~S() S::~S() S::~S() operator delete
对于一个类重载new和delete
为一个类重载new和delete的时候,尽管不必显式的使用static,但是实际上仍是在创建static成员函数。它的语法也和重载任何其它运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的new和delete仍为所有其他类型对象使用(除非它们也有自己的new和delete)。这个和全局变量、局部变量的意思是一样的。
#include<iostream> #include<cstddef> #include<fstream> #include<new> using namespace std; ofstream out("Framis.out"); class Framis { public: enum{psize = 100 }; Framis() {out<< "Framis()" <<endl; } ~Framis() {out<< "~Framis() ... " <<endl; } void* operator new(size_t) throw (bad_alloc); void operator delete(void*); private: enum{sz = 10 }; char c[sz]; static unsigned char pool[]; static bool alloc_map[]; }; unsigned char Framis::pool[psize*sizeof(Framis)]; bool Framis::alloc_map[psize]={false}; void* Framis::operator new(size_t sz) throw(bad_alloc) { for (int i=0; i<psize; ++i) { if (!alloc_map[i]) { out<< "using block " << i << " ... "; alloc_map[i]=true; return pool+(i*sizeof(Framis)); } } out<< "out of memory" <<endl; throw bad_alloc(); } void Framis::operator delete(void* m) { if (!m) return; unsigned long block = (unsigned long)m-(unsigned long)pool; block /= sizeof(Framis); out<< "freeing block " << block <<endl; alloc_map[block]=false; } int main() { Framis* f[Framis::psize]; try { for (int i=0; i<Framis::psize; i++) { f[i]=new Framis; } new Framis; } catch(bad_alloc) { cerr<< "Out of memory!" <<endl; } delete f[10]; f[10]=0; Framis* X=new Framis; delete X; for (int j=0; j<Framis::psize; j++) { delete f[j]; } return 0; }
为数组重载new和delete
上一段文字中我们讲到如果为一个类重载operator new()和operator delete(),那么无论何时创建这个类的一个对象都将调用这些运算符。但是如果要创建这个类的一个对象数组的时候,全局operator new()就会被立即调用,用来为这个数组分配足够的内存。对此,我们可以通过为这个类重载运算符的数组版本,即operator new[]和operator delete[],来控制对象数组的内存分配。
#include<iostream> #include<fstream> #include<new> using namespace std; ofstream trace("ArrayOperatorNew.out"); class Widget { public: Widget() {trace<< "*" <<endl; } ~Widget() {trace<< "~" <<endl; } void* operator new(size_t sz) { trace<< "Widget::new: " << sz << " byte" <<endl; return ::new char[sz]; } void operator delete(void* p) { trace<< "Widget::delete" <<endl; ::delete []p; } void* operator new[](size_t sz) { trace<< "Widget::new[]: " << sz << " bytes" <<endl; return ::new char[sz]; } void operator delete[](void* p) { trace<< "Widget::delete[]" <<endl; ::delete []p; } private: enum{sz=10 }; int an[sz]; }; int main() { trace<< "new Widget" <<endl; Widget* w=new Widget; trace<<endl<< "delete Widget" <<endl; delete w; trace<<endl<< "new Widget[25]" <<endl; Widget* wa=new Widget[25]; trace<<endl<< "delete []Widget" <<endl; delete []wa; return 0; }
C++空类的大小是1