在运行时将一个 ID 解析为其派生类型

3
假设我有100个从基类派生的类。每个派生类在编译时都有一个介于[0,100)之间的唯一标识符。
我有一个函数,它需要接受一个ID并返回具有该ID的新分配实例化的派生类。
Base* CreateDerived(uint32_t id) {
    return new Derived...();
}

显然,为每个ID设置一个巨大的switch case并不是一个好的解决方案。我能想到的最佳解决方案示例如下,但我感觉有一种方法可以避免vtable引入的开销。
struct RegisteredClass {
    static RegisteredClass* ClassTable[MAX_DERIVED_CLASSES];
    static Base* CreateDerived(int ID) { return ClassTable[ID]->Create(); }

    RegisteredClass(int ID) { ClassTable[ID] = this; }
    virtual Base* Create() = 0;
};

template<typename T, int ID>
struct Register : RegisteredClass {
    Register() : RegisteredClass(ID) { }
    virtual Base* Create() { return new T; }
};

class Derived34 : Base {
    static Register<Derived34, 34> _register;
};

我在想,是否有其他方法可以不需要占用这么多空间?关于此问题,您认为我是在胡闹吗?

我认为这是一个可接受的解决方案,除了硬编码边界/ID和访问类表时缺乏范围检查。 - Alex P.
是的,出于演示的目的,我省略了更细节的部分。我认为这种方法是可行的,但我想知道是否存在另一种解决方案(可能使用MPL)。 - Collin Dauphinee
你可以使用boost::uuid来派生出每个类的唯一标识符,而不是使用硬编码的ID,并使用std::map或类似的东西代替数组。但请注意,这将对性能产生影响。 - Alex P.
你确定虚函数表的空间开销是一个大问题吗?假设使用32位操作系统(64位则翻倍),每个虚函数表占用4个字节,ClassTable为每个RegisteredClass添加4个字节,如果您创建了RegisteredClass的池,则每个池最多只需要8或16个字节(取决于对齐方式)。这使得每种类型的最大值为24个字节(数据段和堆),这意味着不到2.5 KB。对于64位操作系统,它可能会增加到5KB。即使在我所知道的大多数嵌入式系统上,这也是相当合理的。 - Asaf
没问题。我确实有内存限制,但并不严重。我主要是对其他方法感到好奇。 - Collin Dauphinee
@AlexP。谢谢。我通过使用指向模板函数的指针成功摆脱了vtable,因此唯一剩下的开销是ID和在运行时填充数组。我想知道constexpr是否有帮助,尽管我的编译器不支持它。 - Collin Dauphinee
2个回答

1
“为每个ID设置一个巨大的switch case显然不是一个好的解决方案。”
“这实际上就是抽象工厂模式,正如你所描述的那样广泛使用。我建议坚持使用它,因为大多数程序员都熟悉它。”
“你的变体似乎过于复杂和难以维护。”

除非可以完全通过模板生成,否则这个开关将难以维护,我甚至都不会考虑它。我所做的最正确的解决方案是使用脚本系统而不是硬编码对象,但目前还不可能。 - Collin Dauphinee
@dauphic 为什么 switch 很难维护? - Luchian Grigore
所有的100种类型都必须手动添加到其中。如果我要添加更多的派生类型,我必须记得将它们添加到开关中。 - Collin Dauphinee
它可能被广泛使用,但如果我有像dauphic这样的100个不同情况,我就不会使用它。 - Alex P.

0

这里有一个情况,旧的陈腐宏功能可以简化任务并消除一些错误。

#define CASE(n) case n: return new Derived##n

Base* CreateDerived(uint32_t id)
{
    switch(id)
    {
        CASE(1);
        CASE(2);
        CASE(3);
        // ...
        default:
            throw std::logic_error("Unhandled Derived## case");
    }
}

#undef CASE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接