c++关于使用new的纠正
自己之前纠正过这个问题,但还是忘了。今天再拿出来。
今天主要总结关于使用 c++ 标准中的 new 关键字。
【结论】
A、处理new可能抛出的异常
B、针对new使用std::nothrow不抛出异常
1、错误示范
下面一段代码,使用new向堆申请空间,再释放的过程
1 char *pbuf = NULL; 2 // 这里有bug, 后面讲 3 pbuf = new char[128]; 4 5 if ( NULL == pbuf) 6 { 7 std::cout << "\n error, pbuf created failure\n"; 8 } 9 10 // 2、doing sth 11 12 // 3、release 13 delete pbuf; 14 pbuf = NULL;
A、代码中 第 3 行 有错误, 准确的说是 这样写不够规范。为什么?
B、因为: 第3行代码 结果有两种:
1)、申请空间成功,并且返回申请成功的地址
2)、 申请失败,则抛出异常: std::bad_alloc (重点),而不是返回 NULL 或者 nullptr. 当然, 上面第4行代码也不会执行。
C、那,怎么改?
2、正确做法
上面分析到了,是因为使用不规范引起的。下面就是规范的写法。修改方法有 2 种。
A、使用 std::nothrow
1 char *pbuf = NULL; 2 // 这里有bug, 后面讲 3 pbuf = new(std::nothrow) char[128]; 4 5 if ( NULL == pbuf) 6 { 7 std::cout << "\n error, pbuf created failure\n"; 8 } 9 10 // 2、doing sth 11 12 // 3、release 13 delete pbuf; 14 pbuf = NULL;
对比上面错误的做法, 变化就在 第3行。 这样就可以了: 申请空间失败, 就会返回 NULL, 当前也会继续执行 第3行以后的代码。
B 、既然 new 可能会抛出异常std::badalloc,那么,我们就需要处理异常。
1 char *pbuf = NULL; 2 try 3 { 4 // 1、需要处理可能抛出的异常 5 pbuf = new char[128]; 6 } 7 catch (const std::bad_alloc& error) 8 { 9 std::cout << "\nerror: " << error.what() << endl; 10 return; 11 } 12 13 // 2、doing sth 14 15 // 3、release 16 delete pbuf; 17 pbuf = NULL;
对比上面的错误示范,变化:
1)、增加了对 new 发出异常的捕获。
2)、去掉了对指针为NULL的判断。
这里的代码, new 失败后, 将不会返回NULL, 而是抛出异常。 需要对异常处理。显然: 判断 if (NULL == pbuf) 就完全失去意义了。
所以,判断new是否返回成功,则需要添加对异常的处理。
3、为什么?
(以下均为个人观点)市面上的编译器 可谓雨后春笋。 new 和 delete 族当然不一样。 但总体原则:
A、 new(std::nothrow) 的方式是沿袭了C的习惯。
B、new 抛出异常的形式 则是 标准呢c++的形式。
C、为什么有这样两种的形式? 直接使用 c++标准的的形式不好吗? 但是需要考虑兼容,C语言中, 判断指针为NULl习惯是个好习惯,可以继续沿袭。而new抛出异常的c++标准方式是对 C的扩展。
D、这样做,既是对C的编程习惯的延续,又扩展了c++关于new定义,满足c++的标准形式。双赢。
4、std::badalloc
这个是? 别急, 定义如下:
1 class bad_alloc 2 : public exception 3 { 4 public: 5 6 bad_alloc() throw() 7 : exception("bad allocation", 1) 8 { 9 } 10 11 private: 12 13 friend class bad_array_new_length; 14 15 bad_alloc(char const* const _Message) throw() 16 : exception(_Message, 1) 17 { 18 } 19 };
其实也是继承的 std::exception 。 这里出现了友元类: bad_array_new_length, 我们再看看他的定义
1 class bad_array_new_length 2 : public bad_alloc 3 { 4 public: 5 6 bad_array_new_length() throw() 7 : bad_alloc("bad array new length") 8 { 9 } 10 };
原来是这样。
5、new/delete 定义
下面是来自VS2015 up3中的new和delete的源码 vcruntime_new.h: 下面的代码 已经掐头去尾
1 namespace std 2 { 3 struct nothrow_t { }; 4 5 extern nothrow_t const nothrow; 6 } 7 #endif 8 9 _Ret_notnull_ _Post_writable_byte_size_(_Size) 10 _VCRT_ALLOCATOR void* __CRTDECL operator new( 11 size_t _Size 12 ); 13 14 _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) 15 _VCRT_ALLOCATOR void* __CRTDECL operator new( 16 size_t _Size, 17 std::nothrow_t const& 18 ) throw(); 19 20 _Ret_notnull_ _Post_writable_byte_size_(_Size) 21 _VCRT_ALLOCATOR void* __CRTDECL operator new[]( 22 size_t _Size 23 ); 24 25 _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) 26 _VCRT_ALLOCATOR void* __CRTDECL operator new[]( 27 size_t _Size, 28 std::nothrow_t const& 29 ) throw(); 30 31 void __CRTDECL operator delete( 32 void* _Block 33 ) throw(); 34 35 void __CRTDECL operator delete( 36 void* _Block, 37 std::nothrow_t const& 38 ) throw(); 39 40 void __CRTDECL operator delete[]( 41 void* _Block 42 ) throw(); 43 44 void __CRTDECL operator delete[]( 45 void* _Block, 46 std::nothrow_t const& 47 ) throw(); 48 49 void __CRTDECL operator delete( 50 void* _Block, 51 size_t _Size 52 ) throw(); 53 54 void __CRTDECL operator delete[]( 55 void* _Block, 56 size_t _Size 57 ) throw(); 58 59 #ifndef __PLACEMENT_NEW_INLINE 60 #define __PLACEMENT_NEW_INLINE 61 _Ret_notnull_ _Post_writable_byte_size_(_Size) 62 inline void* __CRTDECL operator new(size_t _Size, _Writable_bytes_(_Size) void* _Where) throw() 63 { 64 (void)_Size; 65 return _Where; 66 } 67 68 inline void __CRTDECL operator delete(void*, void*) throw() 69 { 70 return; 71 } 72 #endif 73 74 #ifndef __PLACEMENT_VEC_NEW_INLINE 75 #define __PLACEMENT_VEC_NEW_INLINE 76 _Ret_notnull_ _Post_writable_byte_size_(_Size) 77 inline void* __CRTDECL operator new[](size_t _Size, _Writable_bytes_(_Size) void* _Where) throw() 78 { 79 (void)_Size; 80 return _Where; 81 } 82 83 inline void __CRTDECL operator delete[](void*, void*) throw() 84 { 85 } 86 #endif
可以看到 , 上面的代码中 可以看到 声明中存在不会抛出异常的定义,大胆推测,可能是为了延续C的判断指针是否为NULL的扩展。