Single point construction of hierarchical objects in C++






4.59/5 (24 votes)
Prohibit construction of hierarchical objects both on the heap and on the stack, and allow construction from a single construction point in the base class.
Introduction
As it is well known, smart pointers with reference counting in C++ are responsible for sharing object pointers and managing their memory life cycle. When using smart pointers, it is assumed that an internal object pointer that is managed by a smart pointer is not accessible outside the smart pointer. Accessing the internal object pointer outside the smart pointer may lead to an accidental pointer deletion and subsequent memory problems. In order to avoid such problems, it would be great to allow object pointer creation only within the smart pointer and forbid its creation outside the smart pointer.
Background
In one of my projects, we built a framework that introduced a bunch of base classes that the framework works with. The framework makes use of only smart pointers created from these base classes for controlling the class object's memory life cycle. The user may extend the framework by subclassing these base classes; however, the framework utilizes only smart pointers created from these derived user classes. In order to avoid the memory problems described above, a request appeared to disallow derived classes construction on the heap (pointers) as well as on the stack (it is a 'nice to have' ability) and allow only derived class' smart pointer construction. All this has to be done by means of the framework without any assistance from the user; in other words, this should be enforced over the user derived classes.
Code overview
In this article, I show the code that allows the base class to prohibit its derived classes construction on the stack and on the heap from outside the base class and enforce all derived classes to be constructed from a single construction point within the base class.
Here is an example of the Base
class that achieves the declared goal:
class Base
{
public:
class ForbidConstruction
{
private:
ForbidConstruction()
{}
ForbidConstruction(const ForbidConstruction&);
ForbidConstruction& operator=(const ForbidConstruction&);
friend class Base;
};
Base(const ForbidConstruction& /* dummy parameter */)
{
// Do some construction activity
...
}
virtual ~Base() {}
template <typename T>
static SmartPtr<T> create()
{
return SmartPtr<T>( new T( Base::ForbidConstruction() ) );
}
private:
Base(const Base&);
Base& operator=(const Base&);
};
class Derived: public Base
{
public:
Derived(const Base::ForbidConstruction& dummy, /* other constructor parameters */) :
Base(dummy)
{
// Do some construction activity
...
}
...
};
int main()
{
SmartPtr<Derived> derived = Base::create<Derived>();
}
The class Base
has a public constructor that accepts an internal class ForbidConstruction
reference. Note that the internal class ForbidConstruction
has a private constructor and thus can be created only within class Base
that is declared as its friend. Any class that is derived from the Base
class should pass the ForbidConstruction
object to the Base
class constructor. However, the derived class is not able to construct the ForbidConstruction
object itself, so the only option is to accept the ForbidConstruction
object as the derived class' constructor parameter.
Since the ForbidConstruction
object can not be created anywhere outside the Base
class, the Derived
object can be created neither on the stack nor on the heap (pointer) outside the Base
class. So the only way to construct the Derived
object is from within the Base
class. The Base
class provides a static templated create()
method that creates the Derived
object pointer and returns it wrapped within a smart pointer SmartPtr
class.
With the above preparations made in the Base
class, we are able to create a robust framework that does not suffer from an accidental derived class pointer deletion.