0

正如标题所示,我正在寻找一种快速的运行时类型检查方法。为了说明我的问题,假设您有一个如下所示的类层次结构:

      Base
     /     \
    A       D
   / \     / \
  C   B   F   E
       \ /
        G

我的程序将任何类的所有实例作为 Base_ptr 保存在一个列表中,因为所有这些类都共享共同的任务。现在在某些时候,一些派生类需要知道另一个类的实例的存在。到目前为止一切顺利,我知道 dynamic_cast 和 typeid()-operator,但两者都有一些主要缺点:

  1. 如果类型不兼容,dynamic_cast 会消耗大量处理时间(例如,尝试将 E 的实例转换为 C)
  2. typeid() 在“isTypeOrSubtype”情况下不起作用,例如,您需要 D 的所有实例或从 D 派生(所以 Es、Fs 和 Gs 也是如此)

如果此测试成功返回,理想的解决方案将是某种“isTypeOrSubtype”测试并且仅进行强制转换。我有一个自己的方法,其中包含一些宏定义和预先计算的类名哈希,但它非常丑陋且难以维护。所以我正在寻找一种更干净、更快速的动态类型和子类型检查方法,每秒可以检查超过 2000 万次。

4

4 回答 4

0

前段时间我用过这样的东西:

// the actual type is irrelevant, const char*, int, ...
// but const char* is great for debugging, when it contains the actual class name
typedef const char* TypeId;

class Base {

    // actually the type id is not the value, but its memory location !
    // the value is irrelevant (but its a good idea to set it to the class name)
    static TypeId s_myTypeId;

public:

    static TypeId* getClassType()          { return &s_myTypeId; }
    virtual TypeId* getInstanceType()      { return &s_myTypeId; }

    static TypeId* getClassBaseType()      { return NULL; }
    virtual TypeId* getInstanceBaseType()  { return NULL; }

    virtual bool isType( TypeId* type )            { return type==getInstanceType(); }
    virtual bool isTypeOrSubType( TypeId* type )   { return isType(type); }

};


template< class MyBase >
class TBase : public MyBase {

    // actually the type id is not the value, but its memory location !
    // the value is irrelevant (but its a good idea to set it to the class name)
    static TypeId s_myTypeId;

public:

    static TypeId* getClassType()          { return &s_myTypeId; }
    virtual TypeId* getInstanceType()      { return &s_myTypeId; }

    static TypeId* getClassBaseType()      { return MyBase::getClassType(); }
    virtual TypeId* getInstanceBaseType()  { return MyBase::getInstanceType(); }

    virtual bool isType( TypeId* type )            { return type==getInstanceType(); }
    virtual bool isTypeOrSubType( TypeId* type )   { return isType(type) || MyBase::isTypeOrSubType(type); }

};

// by deriving from TBase<Base>, a new instantiation of s_myTypeId was created,
// so the class now has its very own unique type id,
// and it inherited all the type resolution magic
class A : public TBase<Base> {
};

// NOTE: class B must not derive directly from A, but from TBase<A>
// imagine a hidden class between B and A,
// actually B inherits from the TBase<A> instantiation, which in turn inherits from A
class B : public TBase<A> {
};

// you will also need to instantiate the static members
// hereby the memory location will be reserved,
// and on execution that memory location becomes the unique type id
#define IMPLEMENT_RTTI(CL) TypeId CL::s_myTypeId = STRFY(CL)

// one per class per source file:
IMPLEMENT_RTTI(Base);
IMPLEMENT_RTTI(A);
IMPLEMENT_RTTI(B);

// example usage:
A a;
B b;

b.getInstanceType()==B::getClassType();     // TRUE
b.getInstanceBaseType()==A::getClassType(); // TRUE
B::getClassBaseType()==A::getClassType();   // TRUE

b.isType( B::getClassType() );              // TRUE
b.isType( A::getClassType() );              // FALSE

b.isTypeOrSubType( B::getClassType() );     // TRUE
b.isTypeOrSubType( A::getClassType() );     // TRUE
b.isTypeOrSubType( Base::getClassType() );  // TRUE

它安全、快速且易于使用。你只需要遵守两条规则:

  • 不要直接从 aclass X继承,而是从TBase<X>,
  • 并添加IMPLEMENT_RTTI(Me)到源文件。

有一个缺点:它还不支持多重继承。但这将有可能进行一些更改。

并且可能TypeId类型应该像typedef const char* TypeLocand一样组成typedef TypeLoc* TypeId。也许只是品味问题。

于 2013-09-12T04:14:44.867 回答
0

我为我自己的问题写了一个答案,因为这是一种避免 RTTI 的不同方法,但对动态类型/子类型检查的快速方法没有真正的答案。这仍然不是一个干净的解决方案,但到目前为止我能想到的最好的解决方案。

如果这个层次结构中的每个类都具有以下特征,我可以跳过大部分 RTTI。

  1. 每个类都应该有一个私有成员:线程安全向量static SecureVector<[class]*> s_Instances;在哪里。SecureVector<T>
  2. 在每个构造函数结束时,s_Instances.push_back(this);应该调用,以跟踪该类的新创建实例
  3. 在析构函数的开头,s_Instances.erase(this);应该调用,以删除此实例引用
  4. 每个类都应该有一个公共函数:static const SecureVector<[class]*>& Instances() { return s_Instances; }获取一个不可修改的向量,其中包含该类或任何派生类的所有实例

这样做是,每次调用构造函数时,实例都会将自己添加到自己的实例列表中。当派生类调用它们的超构造函数时,超类将自身添加到其各自的实例列表中。例如,如果我在上述层次结构中随机创建 100 个实例,那么我的BaseInstances()向量中总会有 100 个条目。

在代码中,这看起来像这样:

class Base
{
    static SecureVector<Base*> s_Instances; // 1. condition

public:

    Base() 
    {
        s_Instances.push_back(this);    // 2. condition
    }

    ~Base()
    {
        s_Instances.erase(this);    // 3. condition
    }

    static const SecureVector<Base*>& Instances() { return s_Instances; } // 4. condition
};

这仍然是一种解决方法,因为必须手动添加四个条件(或通过宏或类似的东西)。

于 2013-09-11T23:47:35.577 回答
0

如果您的程序知道将要测试的所有子类型,则可以使用返回指向子类型的指针的虚拟接口。正如downvotes 和评论所指出的,这不是最灵活的方法,因为它要求基类了解所有派生类。但是,它非常快。因此,需要在灵活性与性能之间进行权衡。

class Base {
    //...
    virtual A * is_A () { return 0; }
    virtual B * is_B () { return 0; }
    //...
    template <typename MAYBE_DERIVED>
    MAYBE_DERIVED * isTypeOrSubtype () {
        //...dispatch to template specialization to call is_X()
    }
};

//...
class A : virtual public Base {
    //...
    A * is_A () { return this; }
};

在 IDEONE 上,建议的技术使用动态转换快 20 到 50 倍。1实现使用宏允许将新类添加到单个位置,然后以自动方式对基类进行适当的扩展。

(1) - 我最初将它的时钟速度提高了近 100 倍,但这没有isTypeOrSubtype()我添加的用于模拟所需接口的包装器方法。

如果灵活性比性能具有更高的价值,那么性能稍差的解决方案是使用 amap来关联类型和相应的指针值(拥有指针值消除了对动态转换的需要)。实例在map基类中维护,关联由子类的构造函数进行。使用常规map还是 aunordered_map取决于有多少子类实际上继承了基类。我想这些数字会很小,所以一个普通的map就足够了。

class Base {
    std::map<const char *, void *> children_;
    //...
    template <typename MAYBE_DERIVED>
    MAYBE_DERIVED * isTypeOrSubtype () {
        auto x = children_.find(typeid(MAYBE_DERIVED).name());
        return ((x != children_.end())
                ? static_cast<MAYBE_DERIVED *>(x->second)
                : 0);
    }
};

//...
class A : virtual public Base {
    //...
    A () { children_[typeid(A).name()] = this; }
    //...
};

在 IDEONE 上,第二个建议比使用 dynamic cast快 10 到 30 倍。我不认为 IDEONE 编译时会进行优化,所以我希望时间更接近于生产构建的第一个建议。实现的机制用作typeid(...).name()映射的键​​。2

(2) - 这假定typeid(...).name()返回类似于字符串文字的内容,并且在对相同类型进行操作时始终返回相同的字符串指针。如果您的系统不是这样运行的,您可以修改map以将 astd::string作为键,但性能会降低。

于 2013-09-11T01:11:56.270 回答
-1

dynamic_cast会为此工作得很好!

Base *instance = //get the pointer from your collection;
A* ap = dynamic_cast<A*>(instance);
D* dp = dynamic_cast<D*>(instance);

if(ap) {
    //instance is an A or a subclass of A
}
if(dp) {
    //instance is a D or a subclass of D
}

这也适用于更具体的检查。所以你可以检查你想要的任何类型。

于 2013-09-11T01:39:52.310 回答