是否有更好的方法来处理为运行时使用分配层次结构中的类的身份?

时间:2012-09-24 21:26:17

标签: c++ oop polymorphism typeid

我正在努力为我的事件系统做一个可靠的实现。为了识别事件类型, 我有所谓的“类型路径”,它通过层次结构标识事件类型的路径。 这样,我可以处理一个地方的所有InputEvent,无论它们是按键,鼠标输入, 或其他任何东西。但是,一个棘手的问题是为事件类型提供了自己的身份。我最喜欢的是什么 最近完成的是通过让每个实例从静态成员函数中检索叶标识来完成此操作 Event类,仅用作执行此功能以外的接口。 但是,确保每个类型在此结构中只有一个标识的最简单方法 似乎是使用基于类型路径的映射(最多但不包括叶标识/标识符) 和typeid()。hash_code()。

具体来说,我想要的是一个可以轻松添加事件的系统 不得不查阅一堆信息或执行大量愚蠢的样板垃圾。考虑到这一点 (也许可能是我没想到的事情,我应该想要吗?),

  1. 这种设计有何明显的缺陷?
  2. 是否有比使用typeid()更好的方式?我已经阅读了一些关于它的内容,似乎被视为某种东西,取决于被问及其意见的人,应该永远不会被使用或者几乎不会使用 。因为我可能只是生锈或愚蠢,我想知道是否有人知道一个解决方案,如果没有别的,不那么不确定(显然有些类型的(/)是非常糟糕的,虽然我不喜欢知道哪个或者它是否足够重要而且非常重要。)
  3. 我现在拥有的相当简单的例子:

    #include <iostream>
    #include <vector>
    #include <typeinfo>
    #include <map>
    
    void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }
    
    class Foo
    {
    public:
        Foo() {}
        virtual ~Foo() {}
        static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
        {
            static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
            std::size_t typehash = typeid(*evt).hash_code();
            if (typeMap.find(typePath) == typeMap.end())
            {
                unsigned int val = typeMap[typePath].size();
                typeMap[typePath][typehash] = val;
                return val;
            }
            else
            {
                if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
                {
                    unsigned int val = typeMap[typePath].size();
                    typeMap[typePath][typehash] = val;
                    return val;
                }
                return typeMap[typePath][typehash];
            }
        }
        virtual void test() { std::cout << "Foo" << std::endl; }
    protected:
        std::vector<unsigned int> m_typePath;
    };
    
    class Bar : public Foo
    {
    public:
        Bar()
        {
            m_typePath.push_back(Foo::getSubtype(m_typePath, this));
            test();
        }
        virtual ~Bar() {}
        virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
    };
    
    class Baz : public Foo
    {
    public:
        Baz()
        {
            m_typePath.push_back(Foo::getSubtype(m_typePath, this));
            test();
        }
        virtual ~Baz() {}
        virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
    };
    
    class Qux : public Baz
    {
    public:
        Qux()
        {
            m_typePath.push_back(Foo::getSubtype(m_typePath, this));
            test();
        }
        virtual ~Qux() {}
        virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
    };
    
    int main()
    {
        Foo foo0;
    std::cout << "----" << std::endl;
        Bar bar0;
    std::cout << "----" << std::endl;
        Baz baz0;
    std::cout << "----" << std::endl;
        Qux qux0;
    }
    

    输出:

    ----
    Bar: 0,
    ----
    Baz: 1,
    ----
    Baz: 1,
    Qux: 1,0,
    

    这个和其他测试表现出理想的行为,要清楚 编辑:以前的标题并不符合我的意思。 可能相关的注意事项:这是针对库的一部分,并且是一个高度平行的库。为了简化表示设计,我省略了与并发相关的代码,但这些信息也可能对设计有用。另请注意,我仍然只是在创建/分配类型标识符方面寻求帮助;我提到这些是因为某些设计可能因其隐含的约束而不适用。

    赢编辑: 好吧,我的实现速度非常快,完全符合我的需要。使用一些派生类,我可以实例化每个线程一千万个(我在TBB中添加了一些其他测试;它可能会或者可能不会使用正好八个线程,但它会喜欢)遍布派生类,每个都有两个或更多元素它的路径,通常低于.02s。原始实现管理大约四到五秒,取决于容器等,并且只是愚蠢。结果(无论如何都足以得出想法):

    template<typename T> class EventID
    {
    public:
        static const std::size_t typeID;
    };
    
    template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();
    
    class Foo
    {
    public:
        Foo()
        {
            m_typePath.push_back(EventID<Foo>::typeID);
        }
    protected:
        neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
    };
    
    class Bar : public Foo
    {
    public:
        Bar()
        {
            m_typePath.push_back(EventID<Bar>::typeID);
        }
    };
    

2 个答案:

答案 0 :(得分:0)

我会依赖编译器维护的类层次结构,以及类型代码列表:

typedef enum { EVENT, INPUT, MOUSE, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYBOARD, KEYDOWN, KEYUP } EventType;

typedef std::vector<EventType> ETVector;

class Event
{
public:
    virtual void appendType(ETVector& v) { v.push_back(EVENT); }
};

class InputEvent : public Event
{
public:
    virtual void appendType(ETVector& v) { v.push_back(INPUT); Event::appendType(v); }
};

class MouseEvent : public InputEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSE); InputEvent::appendType(v); }
};

class MouseDownEvent : public MouseEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSEDOWN); MouseEvent::appendType(v); }
};

class MouseUpEvent : public MouseEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSEUP); MouseEvent::appendType(v); }
};

class MouseMoveEvent : public MouseEvent
// . . .

class KeyboardEvent : public InputEvent
// . . .

class KeyDownEvent : public KeyboardEvent
// . . .

class KeyUpEvent : public KeyboardEvent
// . . .

然后进行测试,你会得到类似的东西:

KeyUpEvent kue;

EventTypeVector type_path;

kue.appendType(type_path);

for (EventTypeVector::const_iterator i = type_path.begin(); i != type_path.end(); i++)
{
    cout << *i << endl;
}

矢量存储您的类型路径。没有运行时存储成本,也没有static个变量。可以使用typeid而不是手动维护的枚举,但是使用枚举,一切都在您的控制之下,您可以轻松避免与程序中的其他类型冲突。或者,也许可以通过模板使用appendType()方法填充类,以进一步减少代码。

appendType()中明确命名父类是有点乏味的,但由于多重继承,我在C ++中没有别的办法。在Java中我可以使用super,尽管反射可能是更好的方法。

这比你拥有的更简单,还是我错过了什么?祝你好运。

答案 1 :(得分:0)

请参阅最终编辑原始帖子以获得解决方案。