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

Boost 秘密揭秘:Checked Delete

评分:3.2/5 (22 票)
*****
Boost 秘密揭秘
Boost C++ 库 ( https://boost.ac.cn )
主题 / 惯用法: Checked Delete
贡献者: Peter Dimov, Daniel Frey, Howard Hinnant
Boost 参考文件: https://boost.ac.cn/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 的逐行解析与上述基本相同。