使用RTTI确定C++中的继承图?

trb*_*abb 18 c++ inheritance rtti typeid

有什么(如果有的话)c ++构造用于在运行时列出类的祖先?

基本上,我有一个类,它存储一个指向任何对象的指针,包括可能是一个原始类型(有点像boost::any,我不想使用它,因为我需要保留我的对象的所有权).在内部,这个指针是一个void*,但是这个类的目标是void*用运行时类型安全包装.赋值运算符是模板化的,因此在赋值时我取出typeid()传入指针并存储它.然后,当我稍后退回时,我可以检查typeid()存储的类型type_info.如果它不匹配,则演员将抛出异常.

但是有一个问题:我似乎失去了多态性.让我们说B是一个基础D.如果我将一个指针存储D在我的类中,那么存储的type_info也将是D.然后,我可能想要检索一个B指针.如果我使用我的类的方法来强制转换B*,那么typeid(B) == typeid(D)失败,并且D->B转换会引发异常,即使转换是安全的.Dynamic_cast<>()这里不适用,因为我是在一个void*而不是一个B或多个祖先的运作D.

我希望能做的就是检查is_ancestor(typeid(B), typeid(D)).这可能吗?(这不是dynamic_cast<>在幕后做的吗?)

如果没有,那么我正在考虑采取第二种方法:实现一个类TypeInfo,其派生类是模板化的单例.然后,我可以在这些类中存储我喜欢的任何信息,然后在我的AnyPointer班级中保留指向它们的指针.这将允许我以更易于访问的方式在编译时生成/存储祖先信息.因此失败的选项#1(列出祖先的内置方式仅给出运行时可用的信息),是否有一个我可以使用的构造/过程,它将允许在编译时自动生成和存储祖先信息,最好没有不必明确输入的是"类A派生自BC; C从导出D等."?一旦我有这个,有没有一种安全的方式来实际执行该演员表?

Cas*_*eri 11

我有一个类似的问题,我通过例外解决了!我写了一篇关于此的文章:

http://drdobbs.com/cpp/229401004

好.继彼得的建议后,该想法的大纲如下.它依赖于以下事实:如果D派生BD抛出指针,B则会激活期望指针的catch子句.

然后可以编写一个类(在我的文章中我称之为any_ptr),其模板构造函数接受T*并将其副本存储为void*.该类实现了一种静态void*转换为其原始类型T*并抛出结果的机制.期望U*where U= T或是U其基础的catch子句T将被激活,并且该策略是在原始问题中实现测试的关键.

编辑:( 由Matthieu M.提供答案是最好的自给自足,请参阅Dobbs博士的完整答案)

class any_ptr {

    void* ptr_;
    void (*thr_)(void*);

    template <typename T>
    static void thrower(void* ptr) { throw static_cast<T*>(ptr); }

public:

    template <typename T>
    any_ptr(T* ptr) : ptr_(ptr), thr_(&thrower<T>) {}

    template <typename U>
    U* cast() const {
        try { thr_(ptr_); }
        catch (U* ptr) { return ptr; }
        catch (...) {}
        return 0;
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 哇,我希望我早点看到这个答案.我不知道是否将此标记为天才或疯狂的壮举; 我想这都是.我更喜欢不在这里涉及`shared_ptr`,而是让'any_ptr`表现得像`unique_ptr`,前提是OP使用C++ 11 ......但核心机制仍然引人注目. (3认同)

Fle*_*exo 5

信息(通常)在实施中.虽然没有标准的C++方式来访问它,但它没有暴露出来.如果您愿意将自己绑定到特定的实现或实现集,您可以玩脏游戏来查找信息.

使用Itanium ABI的gcc示例如下:

#include <cassert>
#include <typeinfo>
#include <cxxabi.h>
#include <iostream>

bool is_ancestor(const std::type_info& a, const std::type_info& b);

namespace {
  bool walk_tree(const __cxxabiv1::__si_class_type_info *si, const std::type_info& a) {
    return si->__base_type == &a ? true : is_ancestor(a, *si->__base_type);
  }

  bool walk_tree(const __cxxabiv1::__vmi_class_type_info *mi, const std::type_info& a) {
    for (unsigned int i = 0; i < mi->__base_count; ++i) {
      if (is_ancestor(a, *mi->__base_info[i].__base_type))
        return true;
    }
    return false;
  }
}

bool is_ancestor(const std::type_info& a, const std::type_info& b) {
  if (a==b)
    return true;
  const __cxxabiv1::__si_class_type_info *si = dynamic_cast<const __cxxabiv1::__si_class_type_info*>(&b);
  if (si)
    return walk_tree(si, a);
  const __cxxabiv1::__vmi_class_type_info *mi = dynamic_cast<const __cxxabiv1::__vmi_class_type_info*>(&b);
  if (mi)
    return walk_tree(mi, a);
  return false;
}

struct foo {};

struct bar : foo {};

struct baz {};

struct crazy : virtual foo, virtual bar, virtual baz {};

int main() {
  std::cout << is_ancestor(typeid(foo), typeid(bar)) << "\n";
  std::cout << is_ancestor(typeid(foo), typeid(baz)) << "\n";
  std::cout << is_ancestor(typeid(foo), typeid(int)) << "\n";
  std::cout << is_ancestor(typeid(foo), typeid(crazy)) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

我将其type_info转换为内部使用的实际类型,然后递归地使用它来遍历继承树.

我不建议在实际代码中执行此操作,但作为实现细节的练习,这并非不可能.