When calling delete
on an object of incomplete type, the calling code does not have enough information to do the action properly (it
does not know if this object has a trivial or a nontrivial destructor, if it has overloaded the delete
operator…). Therefore, deleting a
pointer to such an object can lead to undefined behavior.
Noncompliant code example
class Body;
class Handle {
public:
Handle();
~Handle() {
delete impl; // Noncompliant, Body is incomplete
}
private:
Body * impl;
};
Compliant solution
// In header file
class Body;
class Handle {
public:
Handle();
~Handle();
// Add other special member functions to respect the rule of five
private:
Body * impl;
};
// In implementation file
#include "Handle.h"
#include "Body.h" // Now Body is complete
Handle::~Handle(){
delete impl; // Compliant, at this point "Body" is a complete class
}
Or, with modern resource handling:
// In header file
class Body;
class Handle {
public:
Handle();
~Handle();
private:
std::unique_ptr<Body> impl; //Compliant
};
// In implementation file
#include "Handle.h"
#include "Body.h" // Now Body is complete
Handle::Handle() : impl{new Body{}} {}
Handle::~Handle() = default; // since "Body" is complete, it can be destroyed by unique_ptr