问题:
动态内存申请一定成功吗?
问题:
new语句中的异常是怎么抛出来的?
一般我们会在new_handler函数中进行内存的整理,整理之后再次申请。
问题:
如何跨编译器统一new的行为,提高代码移植性?
全局定义new就是全局new操作符的重载。
最后两种方法是推荐的做法。
使用nothrow时,new失败了会返回空指针。
示例程序:
1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 #include <exception> 5 6 using namespace std; 7 8 class Test 9 { 10 int m_value; 11 public: 12 Test() 13 { 14 cout << "Test()" << endl; 15 16 m_value = 0; 17 } 18 19 ~Test() 20 { 21 cout << "~Test()" << endl; 22 } 23 24 void* operator new (unsigned int size) 25 { 26 cout << "operator new: " << size << endl; 27 28 // return malloc(size); 29 30 return NULL; 31 } 32 33 void operator delete (void* p) 34 { 35 cout << "operator delete: " << p << endl; 36 37 free(p); 38 } 39 40 void* operator new[] (unsigned int size) 41 { 42 cout << "operator new[]: " << size << endl; 43 44 // return malloc(size); 45 46 return NULL; 47 } 48 49 void operator delete[] (void* p) 50 { 51 cout << "operator delete[]: " << p << endl; 52 53 free(p); 54 } 55 }; 56 57 void my_new_handler() 58 { 59 cout << "void my_new_handler()" << endl; 60 } 61 62 void ex_func_1() 63 { 64 new_handler func = set_new_handler(my_new_handler); 65 66 try 67 { 68 cout << "func = " << func << endl; 69 70 if( func ) 71 { 72 func(); 73 } 74 } 75 catch(const bad_alloc&) 76 { 77 cout << "catch(const bad_alloc&)" << endl; 78 } 79 } 80 81 82 int main(int argc, char *argv[]) 83 { 84 ex_func_1(); 85 86 return 0; 87 }
第64行用于将我们自己的new处理函数设置进去并且返回编译器默认的new处理函数,并赋值给func,运行结果如下:
打印结果为0,说明了编译器没有默认的new处理函数。
vs2010的运行结果如下:
bcc编译器的结果如下:
可以看到bcc编译器是有默认的new处理函数的。
而且func函数确实抛出了bad_alloc异常。
第二个实验函数:
81行的new行为调用了Test类重载的new操作符,new操作符返回NULL(这是我们人为设置的),并且在NULL指针上执行了构造函数,这个构造函数中有成员变量的赋值操作,因此,导致了段错误。
上述程序在vs2010上结果如下:
这说明了如果重载的new操作符返回了NULL,那么是不会调用构造函数的。
同样的程序,bcc编译器的行为如下:
和vs的结果是一样的。
这三个编译器的行为是不统一的。
因此,我们要对程序进行改进,如下:
1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 #include <exception> 5 6 using namespace std; 7 8 class Test 9 { 10 int m_value; 11 public: 12 Test() 13 { 14 cout << "Test()" << endl; 15 16 m_value = 0; 17 } 18 19 ~Test() 20 { 21 cout << "~Test()" << endl; 22 } 23 24 void* operator new (unsigned int size) throw() 25 { 26 cout << "operator new: " << size << endl; 27 28 // return malloc(size); 29 30 return NULL; 31 } 32 33 void operator delete (void* p) 34 { 35 cout << "operator delete: " << p << endl; 36 37 free(p); 38 } 39 40 void* operator new[] (unsigned int size) throw() 41 { 42 cout << "operator new[]: " << size << endl; 43 44 // return malloc(size); 45 46 return NULL; 47 } 48 49 void operator delete[] (void* p) 50 { 51 cout << "operator delete[]: " << p << endl; 52 53 free(p); 54 } 55 }; 56 57 void my_new_handler() 58 { 59 cout << "void my_new_handler()" << endl; 60 } 61 62 void ex_func_1() 63 { 64 new_handler func = set_new_handler(my_new_handler); 65 66 try 67 { 68 cout << "func = " << func << endl; 69 70 if( func ) 71 { 72 func(); 73 } 74 } 75 catch(const bad_alloc&) 76 { 77 cout << "catch(const bad_alloc&)" << endl; 78 } 79 } 80 81 void ex_func_2() 82 { 83 Test* pt = new Test(); 84 85 cout << "pt = " << pt << endl; 86 87 delete pt; 88 89 } 90 91 92 int main(int argc, char *argv[]) 93 { 94 //ex_func_1(); 95 ex_func_2(); 96 97 return 0; 98 }
我们在第24、40行的重载函数声明时加上了throw(),这就告诉编译器这个函数不抛出异常。
结果如下:
这样,这三款编译器的行为就统一了。
添加申请数组的实验:
1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 #include <exception> 5 6 using namespace std; 7 8 class Test 9 { 10 int m_value; 11 public: 12 Test() 13 { 14 cout << "Test()" << endl; 15 16 m_value = 0; 17 } 18 19 ~Test() 20 { 21 cout << "~Test()" << endl; 22 } 23 24 void* operator new (unsigned int size) throw() 25 { 26 cout << "operator new: " << size << endl; 27 28 // return malloc(size); 29 30 return NULL; 31 } 32 33 void operator delete (void* p) 34 { 35 cout << "operator delete: " << p << endl; 36 37 free(p); 38 } 39 40 void* operator new[] (unsigned int size) throw() 41 { 42 cout << "operator new[]: " << size << endl; 43 44 // return malloc(size); 45 46 return NULL; 47 } 48 49 void operator delete[] (void* p) 50 { 51 cout << "operator delete[]: " << p << endl; 52 53 free(p); 54 } 55 }; 56 57 void my_new_handler() 58 { 59 cout << "void my_new_handler()" << endl; 60 } 61 62 void ex_func_1() 63 { 64 new_handler func = set_new_handler(my_new_handler); 65 66 try 67 { 68 cout << "func = " << func << endl; 69 70 if( func ) 71 { 72 func(); 73 } 74 } 75 catch(const bad_alloc&) 76 { 77 cout << "catch(const bad_alloc&)" << endl; 78 } 79 } 80 81 void ex_func_2() 82 { 83 Test* pt = new Test(); 84 85 cout << "pt = " << pt << endl; 86 87 delete pt; 88 89 pt = new Test[5]; 90 91 cout << "pt = " << pt << endl; 92 93 delete[] pt; 94 } 95 96 int main(int argc, char *argv[]) 97 { 98 // ex_func_1(); 99 ex_func_2(); 100 101 return 0; 102 }
结果如下:
linux:
vs2010:
bcc:
实验三:
在单次申请的时候告诉编译器,不管结果是什么都不要抛出异常,如果申请失败返回空指针。
1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 #include <exception> 5 6 using namespace std; 7 8 class Test 9 { 10 int m_value; 11 public: 12 Test() 13 { 14 cout << "Test()" << endl; 15 16 m_value = 0; 17 } 18 19 ~Test() 20 { 21 cout << "~Test()" << endl; 22 } 23 24 void* operator new (unsigned int size) throw() 25 { 26 cout << "operator new: " << size << endl; 27 28 // return malloc(size); 29 30 return NULL; 31 } 32 33 void operator delete (void* p) 34 { 35 cout << "operator delete: " << p << endl; 36 37 free(p); 38 } 39 40 void* operator new[] (unsigned int size) throw() 41 { 42 cout << "operator new[]: " << size << endl; 43 44 // return malloc(size); 45 46 return NULL; 47 } 48 49 void operator delete[] (void* p) 50 { 51 cout << "operator delete[]: " << p << endl; 52 53 free(p); 54 } 55 }; 56 57 void my_new_handler() 58 { 59 cout << "void my_new_handler()" << endl; 60 } 61 62 void ex_func_3() 63 { 64 int* p = new(nothrow) int[10]; 65 66 // ... ... 67 68 delete[] p; 69 70 int bb[2] = {0}; 71 72 struct ST 73 { 74 int x; 75 int y; 76 }; 77 78 ST* pt = new(bb) ST(); 79 80 pt->x = 1; 81 pt->y = 2; 82 83 cout << bb[0] << endl; 84 cout << bb[1] << endl; 85 86 pt->~ST(); 87 } 88 89 int main(int argc, char *argv[]) 90 { 91 92 ex_func_3(); 93 94 return 0; 95 }
第64行告诉编译器无论成功失败都不要抛出异常,第78行是在指定的位置创建对象,在这里我们是通过new在栈上创建对象,在第86行我们手动的调用析构函数。在这里不能delete pt指针。
结果如下:
bcc下结果:
vs结果:
可以看到三款编译器的结果全部一致。
vs2010的第一个版本new实现如下:
第29行申请代码,第33行判断,申请成功就会跳出去直接返回。第43行的是调试代码,可以忽略。
申请不成功会走到第37行,会调用到_calnewh函数,_callnewh说明文档中给出如下解释:
如果没有设置全局的new处理函数,那么就会抛出bad_alloc异常,如果设置的new处理函数没有进行内存整理,那么_callnewh就会返回0,最终返回给用户空指针。
因此如果我们自定义new_handler函数,就能使new不抛出异常。这个解决方案对于vs编译器是有用的。
vs中new的另一个实现:
这个函数就是malloc失败就会进入if语句,然后调用_callnewh函数,如果这个函数没有成功处理内存申请的问题,那么就抛出bad_alloc异常。
小结: