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的扩展。

 

posted @ 2020-08-17 20:12  mohist  阅读(516)  评论(0编辑  收藏  举报