c++ RTTI注册模式

C++ RTTI Registry Pattern

本文关键字:模式 注册 RTTI c++      更新时间:2023-10-16

我有兴趣了解更多关于c++中RTTI的优点和局限性。假设我有以下场景:

class C {};
class C0 : public C {};
class C1 : public C {};
...
void print(int id, C* c) {
    if (id == 0) dynamic_cast<C0 *>(c)->print();
    else if (id == 1) dynamic_cast<C0 *>(c)->print();
    ...
}

是否有可能使用注册表模式实现上面的示例?例如,使用这样的代码:

map<int, ?> registry;
void print(int id, C* c) {
    registry[id](c)->print();
}

这很容易通过使print成为虚函数来解决。然后你可以简单地输入:

void print(C* c)
{
    c->print();
}

,它会为所有的派生类做正确的事情。

但是如果你想保持print非虚拟,那么你就会意识到registry[id](c)->print();这样的东西是如何工作的。映射的值类型是编译时的事实,但您希望运行时的行为有所不同。

好吧,我可以想到一个方法……通过使用虚函数。您需要创建一个类,作为C的包装器,其派生版本与从C派生的类型匹配。使用一些模板可能会使这个问题更容易处理。然后,根据指向基类的指针声明映射,但通过派生包装器填充映射。

但最终,它带来了更多的复杂性,并没有提供比首先将print本身设置为虚拟所能实现的更多的好处。

虽然使用多态性和虚拟方法似乎更合适,但您可以使用以下内容来根据id

进行注册和分派
class PrintCaller
{
public:
    template <typename T>
    std::size_t register_class()
    {
        m.push_back([](C* c) {
            auto* p = dynamic_cast<T*>(c);
            if (p) {
                p->print();
            } else {
                throw std::runtime_error("Incorrect type");
            }
        });
        return m.size() - 1;
    }
    void print(std::size_t id, C* c) const {
        if (id < m.size()) {
            m[id](c);
        } else {
            throw std::runtime_error("invalid id");
        }
    }
private:
    std::vector<std::function<void(C*)>> m;
};

生活例子