Rule A5-3-3 (required, implementation, automated)
Pointers to incomplete class types shall not be deleted.
Rationale
Incomplete class types are forward declared class types, for which the compiler has not yet seen a definition. It is undefined behavior if a pointer to an incomplete class type is deleted, and the class has a non-trivial destructor or a deallocation function. This rule prohibits deletion of such a pointer even in the harmless case of a trivially destructible class type without a deallocation function, since a non-trivial destructor or a deallocation function may be added later as the code evolves.
Example
// $Id: A5-3-3.cpp 309184 2018-02-26 20:38:28Z jan.babst $
// Non-compliant: At the point of deletion, pimpl points
// to an incomplete class type.
class Bad
{
class Impl;
Impl* pimpl;
public:
// ...
~Bad() { delete pimpl; } // violates A18-5-2
};
// Compliant: At the point of deletion, pimpl points to
// a complete class type.
// In a header file ...
class Good
{
class Impl;
Impl* pimpl;
public:
// ...
~Good();
};
// In an implementation file ...
class Good::Impl
{
// ...
};
// Good::Impl is a complete type now
Good::~Good()
{
delete pimpl; // violates A18-5-2
}
// Compliant: Contemporary solution using std::unique_ptr
// and conforming to A18-5-2.
// Note that std::unique_ptr<Impl> requires Impl to be a complete type
// at the point where pimpl is deleted and thus automatically enforces
// A5-3-3. This is the reason why the destructor of Better must be defined in an
// implementation file when Better::Impl is a complete type, even if the
// definition is just the default one.
// In a header file ...
#include <memory>
class Better
{
class Impl;
std::unique_ptr<Impl> pimpl;
public:
// ...
~Better();
};
// In an implementation file ...
class Better::Impl
{
// ...
};
// Better::Impl is a complete type now
Better::~Better() = default;
See also
ISO/IEC 14882:2014 [3]: 5.3.5: [expr.delete] SEI CERT C++ Coding Standard [10]: EXP57-CPP: Do not cast or delete pointers to incomplete classes.