前言
上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载new和delete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家。
通常我们为了一些目的而使用new和delete的内存分配系统,但是在特殊情况下,它并不能够满足需要。最常见的改变分配系统的原因是出于效率考虑:也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。C++允许重载new和delete来实现我们自己的存储分配方案,所以可以用它来处理问题。
另一个问题就是堆碎片:分配不同大小的内存可能会在堆上产生很多碎片,以至于很快用完内存。虽然内存可能还有,但是由于都是碎片,也就找不到足够大的内存块满足需要。通过为特定类创建自己的内存分配器,可以确保这种情况不会发生。
当我们在重载operator new()和operator delete()时,我们只是改变了原有的内存分配方法。编译器将用重载的new代替默认版本去分配内存,然后为那个内存调用构造函数。所以,虽然当编译器看到new时,编译器分配内存并调用构造函数,但是当重载new时,可以改变的只是内存分配部分。
接下来,我分为三个部分详细讲解重载new和delete:重载全局new和delete、对于一个类重载new和delete以及为数组重载new和delete。
重载全局new和delete
当我们重载全局的new和delete的时候,可想而知,就会使默认版本完全不能被访问--甚至在这个重新定义里也不能调用它们。
重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。
我们首先需要明白的一点就是:operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define inf 0x7fffffff 8 using namespace std; 9 10 void* operator new(size_t sz) 11 { 12 printf("operator new: %d Bytes\n",sz); 13 void* m = malloc(sz); 14 if (!m) puts("out of memory"); 15 return m; 16 } 17 18 void operator delete(void* m) 19 { 20 puts("operator delete"); 21 free(m); 22 } 23 24 class S 25 { 26 public: 27 S() {puts("S::S()"); } 28 ~S() {puts("S::~S()"); } 29 private: 30 int an[1000]; 31 }; 32 33 int main() 34 { 35 puts("creating & destroying an int"); 36 int* q = new int(23); 37 delete q; 38 puts("creating & destroying an int[]"); 39 int* p = new int[10](); 40 delete []p; 41 puts("creating & destroying an s"); 42 S* s = new S; 43 delete s; 44 puts("creating & destroying S[3]"); 45 S* sa = new S[3]; 46 delete []sa; 47 return 0; 48 }
对于一个类重载new和delete
为一个类重载new和delete的时候,尽管不必显式的使用static,但是实际上仍是在创建static成员函数。它的语法也和重载任何其它运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的new和delete仍为所有其他类型对象使用(除非它们也有自己的new和delete)。这个和全局变量、局部变量的意思是一样的,应该很好懂吧。
1 #include<iostream> 2 #include<cstddef> 3 #include<fstream> 4 #include<new> 5 using namespace std; 6 ofstream out("Framis.out"); 7 8 class Framis 9 { 10 public: 11 enum{psize = 100 }; 12 Framis() {out<< "Framis()" <<endl; } 13 ~Framis() {out<< "~Framis() ... " <<endl; } 14 void* operator new(size_t) throw (bad_alloc); 15 void operator delete(void*); 16 private: 17 enum{sz = 10 }; 18 char c[sz]; 19 static unsigned char pool[]; 20 static bool alloc_map[]; 21 }; 22 unsigned char Framis::pool[psize*sizeof(Framis)]; 23 bool Framis::alloc_map[psize]={false}; 24 25 void* Framis::operator new(size_t sz) throw(bad_alloc) 26 { 27 for (int i=0; i<psize; ++i) { 28 if (!alloc_map[i]) { 29 out<< "using block " << i << " ... "; 30 alloc_map[i]=true; 31 return pool+(i*sizeof(Framis)); 32 } 33 } 34 out<< "out of memory" <<endl; 35 throw bad_alloc(); 36 } 37 38 void Framis::operator delete(void* m) 39 { 40 if (!m) return; 41 unsigned long block = (unsigned long)m-(unsigned long)pool; 42 block /= sizeof(Framis); 43 out<< "freeing block " << block <<endl; 44 alloc_map[block]=false; 45 } 46 47 int main() 48 { 49 Framis* f[Framis::psize]; 50 try { 51 for (int i=0; i<Framis::psize; i++) { 52 f[i]=new Framis; 53 } 54 new Framis; 55 } catch(bad_alloc) { 56 cerr<< "Out of memory!" <<endl; 57 } 58 delete f[10]; 59 f[10]=0; 60 Framis* X=new Framis; 61 delete X; 62 for (int j=0; j<Framis::psize; j++) { 63 delete f[j]; 64 } 65 return 0; 66 }
为数组重载new和delete
上一段文字中我们讲到如果为一个类重载operator new()和operator delete(),那么无论何时创建这个类的一个对象都将调用这些运算符。但是如果要创建这个类的一个对象数组的时候,全局operator new()就会被立即调用,用来为这个数组分配足够的内存。对此,我们可以通过为这个类重载运算符的数组版本,即operator new[]和operator delete[],来控制对象数组的内存分配。
1 #include<iostream> 2 #include<fstream> 3 #include<new> 4 using namespace std; 5 ofstream trace("ArrayOperatorNew.out"); 6 7 class Widget 8 { 9 public: 10 Widget() {trace<< "*" <<endl; } 11 ~Widget() {trace<< "~" <<endl; } 12 void* operator new(size_t sz) { 13 trace<< "Widget::new: " << sz << " byte" <<endl; 14 return ::new char[sz]; 15 } 16 void operator delete(void* p) { 17 trace<< "Widget::delete" <<endl; 18 ::delete []p; 19 } 20 void* operator new[](size_t sz) { 21 trace<< "Widget::new[]: " << sz << " bytes" <<endl; 22 return ::new char[sz]; 23 } 24 void operator delete[](void* p) { 25 trace<< "Widget::delete[]" <<endl; 26 ::delete []p; 27 } 28 private: 29 enum{sz=10 }; 30 int an[sz]; 31 }; 32 33 int main() 34 { 35 trace<< "new Widget" <<endl; 36 Widget* w=new Widget; 37 trace<<endl<< "delete Widget" <<endl; 38 delete w; 39 trace<<endl<< "new Widget[25]" <<endl; 40 Widget* wa=new Widget[25]; 41 trace<<endl<< "delete []Widget" <<endl; 42 delete []wa; 43 return 0; 44 }
总结
1:对于C++中的动态对象创建又有了新的认识,学习了重载new和delete
2:C++空类的大小是1
3:最让我激动的就是:C++程序把运行结果写入新创建的文档里面,这个和ACM里常用的文件读写还是不一样滴。
好吧,继续努力!!!