我想知道是否有人知道一种强制类层次结构只能由工厂构造的方法,从而有效地禁止在工厂外部直接使用std::make_shared
。
在下面的示例中,我将 Node 作为基类,将 SceneNode 作为许多派生类之一。 Node 包含静态成员函数 create(),该函数应该是工厂,并且是创建 Node 派生类的新实例的唯一方法。 >
#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
}
答案 0 :(得分:2)
使工厂成为唯一可以实例化给定类的类的经典方法是使您的类构造函数私有,并使您的工厂成为您的类的朋友:
class Foo
{
friend class FooFactory;
private:
Foo() = default;
};
class FooFactory
{
public:
static Foo* CreateFoo() { return new Foo(); }
static void DestroyFoo(Foo* p_toDestroy) { delete p_toDestroy; }
};
int main()
{
// Foo foo; <== Won't compile
Foo* foo = FooFactory::CreateFoo();
FooFactory::DestroyFoo(foo);
return 0;
}
编辑(具有一定的继承性):
#include <type_traits>
class Foo
{
friend class FooBaseFactory;
protected:
Foo() = default;
};
class Bar : public Foo
{
friend class FooBaseFactory;
protected:
Bar() = default;
};
class FooBaseFactory
{
public:
template <typename T>
static T* Create()
{
static_assert(std::is_base_of<Foo, T>::value, "T must derive from Foo");
return new T();
}
template <typename T>
static void Destroy(T* p_toDestroy)
{
static_assert(std::is_base_of<Foo, T>::value, "T must derive from Foo");
delete p_toDestroy;
}
};
int main()
{
// Foo foo; <== Won't compile
Foo* foo = FooBaseFactory::Create<Foo>();
FooBaseFactory::Destroy<Foo>(foo);
// Bar bar; <== Won't compile
Bar* bar = FooBaseFactory::Create<Bar>();
FooBaseFactory::Destroy<Bar>(bar);
return 0;
}
答案 1 :(得分:1)
解决此问题的一种方法是创建一个只有工厂才能实例化的类型,并要求构造该类的实例来构造基本类型。您可以建立一个约定,其中从Node
派生的类型的第一个构造函数参数是馈给Node
的构造函数的该类型的值或对该类型的引用。由于其他任何人都不可能拥有NodeKey
,因此用户无法在不经过工厂的情况下实例化源自Node
的任何东西。
#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>();
}