带有实例化模板的切换枚举的工厂
Factory with switch over enum that instantiates templates
我在代码库的各个位置都有以下层次结构模式:
enum DerivedType {
A, B, C };
class Base {
public:
static Base* Create(DerivedType t);
};
template <DerivedType T>
class Derived : public Base {
};
Create
方法根据其参数返回Derived<A>
、Derived<B>
或Derived<C>
类的新对象:
Base* Base::Create(DerivedType t) {
switch (t) {
case A: return new Derived<A>;
case B: return new Derived<B>;
case C: return new Derived<C>;
default: return NULL;
}
}
问题是有许多这样的Base -> Derived
层次结构,基本上相同的Create()
副本实现被粘贴到各处。这里有没有一种优雅但易于理解的方法来避免重复?
我们可以抽象掉工厂的详细信息,并依靠客户端为我们提供枚举到类类型的映射:
template<typename ENUM, typename T>
struct Factory
{
typedef std::map<ENUM, T*(*)()> map_type;
static map_type factoryMapping_;
static T* Create(ENUM c)
{
return factoryMapping_[c]();
}
static void Init(map_type _mapping)
{
factoryMapping_ = _mapping;
}
};
template<typename ENUM, typename T>
typename Factory<ENUM, T>::map_type Factory<ENUM,T>::factoryMapping_;
现在,客户端的工作是为我们提供在给定枚举值的情况下创建Base*
的方法。
如果你愿意并且能够用模板抽象出派生类的创建,那么你可以节省相当多的打字。
我的意思是,让我们创建一个模板化函数来创建一个派生类(不需要任何真正的正确性检查):
template<typename Base, typename Derived>
Base* CreateDerived()
{
return new Derived();
}
现在我可以定义一个枚举和相关的类层次结构:
enum ClassType {A, B};
struct Foo
{
virtual void PrintName() const
{
std::cout << "Foon";
}
};
typedef Factory<ClassType, Foo> FooFactory ;
struct DerivedOne : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedOnen";
}
};
struct DerivedTwo : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedTwon";
}
};
然后像这样使用:
// set up factory
std::map<ClassType, Foo*(*)()> mapping;
mapping[A] = &CreateDerived<Foo, DerivedOne>;
mapping[B] = &CreateDerived<Foo, DerivedTwo>;
FooFactory::Init(mapping);
// Use the factory
Foo* f = FooFactory::Create(A);
f->PrintName();
现场演示
当然,这会稍微简化您的问题,即将工厂详细信息从基类中移出,并在一分钟内忽略子级本身是模板化的。根据在您的域中为每种类型创建一个好的CreateDerived
函数的难度,您最终可能不会节省大量的键入。
EDIT:我们可以利用std::map::find修改Create
函数以返回NULL。为了简洁起见,我省略了它。如果你关心性能,那么是的,O(logn)搜索渐进地比简单的切换慢,但我强烈怀疑这最终会成为热门路径。
您可以将其作为模板
template<typename Base, typename Derive, template<Derive> class Derived>
Base* Base::Create(Derive t) {
switch (t) {
case Derive::A: return new Derived<Derive::A>;
case Derive::B: return new Derived<Derive::B>;
case Derive::C: return new Derived<Derive::C>;
default: return nullptr;
}
}
但假设struct enum Derive
中只有A
、B
和C
。
您可以使用Java样式的枚举,而不是使用C++样式的枚举。在该枚举中,每个值都是从公共基派生的类的单例。
然后在基类上定义一组纯虚拟函数,以创建派生类的所需风格,并在每个singleton中适当地实现它们。
然后可以使用t->createDerived()
而不是switch (t) ...
。
或者更简洁地说:用多态性代替switch。
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 错误:从"int"到枚举c++的转换无效
- C++中构造函数中的枚举
- 访问在 C++ 结构中声明的枚举变量
- 枚举类'classname'的多重定义
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- typedef 枚举和枚举类有什么区别?
- 为什么我的开关/机箱在使用枚举时默认?
- 标准::可选枚举的比较运算符
- C++两个源文件之间共享的枚举的静态实例
- 打印没有铸件的枚举可以在C++中吗?
- 枚举成员与静态 int 成员?
- C++:枚举:错误:应使用标识符而不是"}"
- 带有 c++ 的枚举(输入检查)
- 在 qml 中使用 Q_ENUM 和 Q_PROPERTY 作为枚举类
- 带有实例化模板的切换枚举的工厂
- 带有模板的基于枚举的工厂无法转换类型