Note - This document may contain errors, if you spot one (or a dozen of them), let me know please.
Section I:What is the purpose?
The checked delete idiom adds a compile-time safety check against deleting incomplete classes. The C++ Standard mandates that a pointer to an incomplete class may be deleted, so long as the class has a trivial desctructor and does not overload operator delete. Deleting pointers to classes with non-trivial destructors, or classes that overload operator delete results in undefined behavior.
/* 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 = newint(4);
}
~A(void)
{
delete x;
}
};
class B { };
class C
{
public:
voidoperatordelete(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 */
Most compilers generate a warning when this occurs, however some compilers have been known not to catch these errors. This is what the checked delete idiom is designed to remedy.
Section III:Line-By-Line Evaluation
The original source is listed at the top of this article.
The following code has been stripped of comments and preprocessor directives.
Boost's implementation of the checked delete idiom consists of two function templates and two class templates.
template<class T> void checked_delete(T* p)
{
typedefchar 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.
Lines 8-16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template<class T> struct checked_deleter
{
typedefvoid result_type;
typedef T * argument_type;
voidoperator()(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.
Lines 19-34: The line-by-line for checked_array_delete is essentially the same as above.