More C++ Idioms/Checked delete

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Checked delete
[edit | edit source]

Intent[edit | edit source]

Increase the safety of the delete expression.

Also Known As[edit | edit source]

Motivation and Sample Code[edit | edit source]

The C++ Standard allows, in 5.3.5/5, pointers to incomplete class types to be deleted with a delete-expression. When the class has a non-trivial destructor, or a class-specific operator delete, the behavior is undefined. Some compilers issue a warning when an incomplete type is deleted, but unfortunately, not all do, and programmers sometimes ignore or disable warnings.

In the following example, main.cpp can see the definition of Object. However, main() calls delete_object(), defined in deleter.cpp, which does not see the definition of Object, but only forward declares it. Calling delete on a partially defined type like this is undefined behavior which some compilers do not flag.

////////////////////
// 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);
}

Solution and Sample Code[edit | edit source]

The Checked Delete idiom relies on calls to a function template to delete memory, which fails for declared but undefined types, rather than calls to delete.

The following is the implementation of boost::checked_delete, a function template in the Boost Utility library. It forces a compilation error by invoking the sizeof operator on the parameterizing type, T. If T is declared but not defined, sizeof(T) will generate a compilation error or return zero, depending upon the compiler. If sizeof(T) returns zero, checked_delete triggers a compilation error by declaring an array with -1 elements. The array name is type_must_be_complete, which should appear in the error message in that case, helping to explain the mistake.

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);
    }
};

NOTE: This same technique can be applied to the array delete operator as well.

WARNING: std::auto_ptr does not use anything equivalent to checked delete. Therefore, instantiating an auto_ptr using an incomplete type may cause undefined behavior in its destructor if, at the point of declaration of the auto_ptr, the template parameter type is not fully defined.

Known Uses[edit | edit source]

Related Idioms[edit | edit source]

References[edit | edit source]