I wanted to know if anyone knows of a way to force a class hierarchy to be constructible only by the factory, effectively prohibiting the direct use of std::make_shared
outside of that factory.
In the example below I have Node as the base class and SceneNode as one of the many derived classes. Node contains a static member function create() which should be the factory and only way to create new instances of Node-derived classes.
#include <iostream>
#include <memory>
class Node {
public:
template <class T, class... Args>
static std::shared_ptr<T> create(Args&&... args)
{
static_assert(std::is_base_of<Node, T>::value, "T must derive from Node");
std::shared_ptr<T> node = std::make_shared<T>(std::forward<Args>(args)...);
return node;
}
protected:
Node() {}
};
class SceneNode : public Node {
public:
SceneNode() : Node()
{
}
};
int main() {
auto a = Node::create<SceneNode>(); // Should be the only way
auto b = std::make_shared<SceneNode>(); // Should be forbidden
}
One solution to this problem is to create a type that only the factory can instantiate, and have an instance of that class be required to construct the base type. You can establish a convention where the first constructor argument for types that derive from Node
is a value of or reference to that type which is fed to Node
's constructor. Since it's not possible for anyone else to have a NodeKey
users can't instantiate anything that derives from Node
without going through the factory.
#include <memory>
#include <utility>
// Class that can only be instantiated by the factory type
class NodeKey {
private:
NodeKey() {};
friend class Factory;
};
class Factory {
public:
template<class T, class ... Args>
auto make(Args&&... args) {
auto ptr = std::make_shared<T>(NodeKey{}, std::forward<Args>(args)...);
// Finish initializing ptr
return ptr;
}
};
class Node {
public:
// Can only be called with an instance of NodeKey
explicit Node(const NodeKey &) {};
};
class Foo : public Node {
public:
// Forwards the instance
explicit Foo(const NodeKey & key) : Node(key) {};
};
int main()
{
Factory factory;
auto f = factory.make<Foo>();
}