更多 C++ 惯用法/Checked delete
提高delete
表达式的安全性。
C++ 标准允许在 5.3.5/5 中,使用 delete-expression 删除指向不完整类类型的指针。当类具有非平凡析构函数或类特定的 operator delete 时,行为是未定义的。一些编译器在删除不完整类型时会发出警告,但不幸的是,并非所有编译器都会这样做,并且程序员有时会忽略或禁用警告。
在以下示例中,main.cpp
可以看到 Object
的定义。但是,main()
调用在 deleter.cpp
中定义的 delete_object()
,它没有看到 Object
的定义,而只是向前声明它。对像这样部分定义的类型调用 delete
是未定义的行为,一些编译器不会标记它。
////////////////////
// File: deleter.hpp
////////////////////
// Declares but does not define Object.
struct Object;
void delete_object(Object* p);
////////////////////
// File: deleter.cpp
////////////////////
#include "deleter.hpp"
// Deletes an Object without knowing its definition.
void delete_object(Object* p)
{
delete p;
}
////////////////////
// File: object.hpp
////////////////////
struct Object
{
// This user-defined destructor won't be called when delete is
// called on a partially-defined (i.e., predeclared) Object.
~Object() {
// ...
}
};
////////////////////
// File: main.cpp
////////////////////
#include "deleter.hpp"
#include "object.hpp"
int main() {
Object* p = new Object;
delete_object(p);
}
Checked Delete 惯用法依赖于对函数模板的调用来删除内存,该模板对于已声明但未定义的类型将失败,而不是对 delete
的调用。
以下是 Boost 效用库中的函数模板 boost::checked_delete 的实现。它通过对参数化类型 T
调用 sizeof
运算符来强制编译错误。如果 T
已声明但未定义,sizeof(T)
将生成编译错误或返回零,具体取决于编译器。如果 sizeof(T)
返回零,checked_delete 通过声明具有 -1 个元素的数组来触发编译错误。数组名称是 type_must_be_complete,在这种情况下,它应该出现在错误消息中,有助于解释错误。
template<class T>
inline void checked_delete(T * x)
{
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x;
}
template<class T>
struct checked_deleter : std::unary_function <T *, void>
{
void operator()(T * x) const
{
boost::checked_delete(x);
}
};
注意:相同的技术也可以应用于数组删除运算符。
警告:std::auto_ptr 不使用任何等效于 checked delete 的东西。因此,如果在声明 auto_ptr 时,模板参数类型未完全定义,则使用不完整类型实例化 auto_ptr 可能会导致其析构函数中的未定义行为。
- Boost 的 checked_delete。