第一部分: 目的是什么?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 的逐行解析与上述基本相同。