• 文章
  • Boost 秘密揭秘:Checked Delete
2011 年 6 月 17 日(最後更新:2011 年 6 月 17 日)

Boost 秘密揭秘:Checked Delete

評分:3.2/5 (22 票)
*****
Boost 秘密揭秘
Boost C++ 庫 ( http://www.boost.org )
主題 / 慣用法: Checked Delete
貢獻者: Peter Dimov, Daniel Frey, Howard Hinnant
Boost 參考檔案: http://www.boost.org/doc/libs/1_46_1/boost/checked_delete.hpp
注意 - 本文件可能包含錯誤,如果您發現任何錯誤(無論是一個還是十幾個),請告知我。

第一部分: 目的是什麼?
Checked delete 慣用法增加了編譯時安全檢查,防止刪除不完整類。C++ 標準規定,指向不完整類的指標可以被刪除,前提是該類具有平凡解構函式沒有過載 operator delete。刪除指向具有非平凡解構函式或過載 operator delete 的類的指標會導致未定義行為

第二部分: 有問題程式碼示例
考慮以下程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* Deleter.hpp */

#include<cstdlib>

class A;
class B;
class C;
void func1(A* a);
void func2(B* b);
void func3(C* c);
/* End of Deleter.hpp */


/* Deleter.cpp */
#include "Deleter.hpp"
void func1(A* a)
{
  delete a;
}

void func2(B* b)
{
  delete b;
}

void func3(C* c)
{
  delete c;
}

/* End of Deleter.cpp */

/* Classes.hpp */
class A
{
  private:
    int* x;

  public:
    A(void)
    {
      x = new int(4);
    }

    ~A(void)
    {
      delete x;
    }
};

class B { };

class C
{
  public:
    void operator delete(void* pointer)
    {
      free(pointer);
    }
};
/* End of Classes.hpp */


/* Main.cpp */
#include "Deleter.hpp"
#include "Classes.hpp"
int main(void)
{
  A* a = new A;
  func1(a); // <-- Undefined Behavior - A has a non-trivial destructor
  B* b = new B;
  func2(b); // <-- Legal - B has a trivial destructor and does not overload operator delete
  C* c = new C;
  func3(c); // <-- Undefined Behavior - C has an overloaded operator delete
  return 0;
}
/* End of Main.cpp */


大多數編譯器會為此類情況生成警告,但據報道有些編譯器不會捕獲這些錯誤。這就是 checked delete 慣用法旨在解決的問題。

第三部分: 逐行解析
原始原始碼列在本文章的頂部。
以下程式碼已移除註釋和預處理器指令。

Boost 對 checked delete 慣用法的實現包括兩個函式模板和兩個類模板。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
template<class T> void checked_delete(T* p)
{
  typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
  (void) sizeof(type_must_be_complete);
  delete p;
}

template<class T> struct checked_deleter
{
  typedef void result_type;
  typedef T * argument_type;
  void operator()(T * x) const
  {
    boost::checked_delete(x);
  }
};


template<class T> void checked_array_delete(T* p)
{
  typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
  (void) sizeof(type_must_be_complete);
  delete [] p;
}

template<class T> struct checked_array_deleter
{
  typedef void result_type;
  typedef T * argument_type;
  void operator()(T * x) const
  {
      boost::checked_array_delete(x);
  }
};


雖然程式碼的主體看起來非常相似,但其中一組用於檢查 operator delete,另一組用於檢查 operator delete[]

警告 - 以下內容包含無法從 Boost 文件引用的觀點。
第 1-6 行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template<class T> void checked_delete(T* p)
{
  typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
  (void) sizeof(type_must_be_complete);
  delete p;
}
// Line 3:
//  sizeof(T) will either evaluate to 0,
//  or cause a compiler error if T is an incomplete type.
//  If sizeof(T) evaluates to 0, then a compiler error is
//  caused by attempting to declare an array of -1 elements.
//
//  Line 4: * Citation Needed
//   While I cannot be sure of why line 4 is there,
//   I assume that it is to prevent the compiler from
//   complaining about an unused variable, while at the
//   same time helping it optimize the build casting the
//   result of sizeof(type_must_be_completed) to void.
//
//  Line 5:
//   operator delete is called on the parameter
//
//  Furthermore, it can be deduced that the defined
//  type, `type_must_be_complete`, is named as such
//  to aid in the debugging process, as it will show
//  up in the compiler output as an indicator of where
//  the problem lies. 


第 8-16 行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<class T> struct checked_deleter
{
  typedef void result_type;
  typedef T * argument_type;
  void operator()(T * x) const
  {
    boost::checked_delete(x);
  }
};
//  Lines 3 & 4:
//   As far as I can see, lines 3 and 4 are present
//   for documentation purposes.
//  Lines 5-8:
//   The overloaded operator() simply calls the corresponding
//   function. 


第 19-34 行
checked_array_delete 的逐行解析與上述基本相同。