15

我目前正致力于在非 RTTI 平台 (Android) 上集成使用大量 RTTI 内容的第三方包。基本上,我做了自己的 RTTI 实现,但我遇到了一个问题。

问题是很多类都有菱形继承问题,因为所有类都派生自同一个基类(对象)。所以,如果我想从基类向下转换为派生类,我必须使用dynamic_cast - 但 RTTI 不可用!当有没有dynamic_cast的虚拟继承时,如何将对象从父对象转换为子对象?

它看起来像这样:

class A 
{
public:
 virtual char* func() { return "A"; };
};
class B : public virtual A
{
public:
 //virtual char* func() { return "B"; };
};
class C : public virtual A 
{
public:
 //virtual char* func() { return "C"; };
};

class D : public B, public C 
{
public:
 //virtual char* func() { return "D"; };
};

D d;
A* pa = static_cast<A*>(&d);
D* pd = static_cast<D*>(pa); // can't do that! dynamic_cast does work though...

这些是我的错误:

错误 C2635:无法将“A*”转换为“D*”;隐含从虚拟基类的转换

错误 C2440:“正在初始化”:无法从“test_convert::A *”转换为“test_convert::D *”从基础转换为派生需要 dynamic_cast 或 static_cast

有任何想法吗?

4

6 回答 6

12

你只能用dynamic_cast;来做这个演员。没有其他演员会这样做。

如果你不能设计你的接口,这样你就不需要执行这种类型的转换,那么你唯一能做的就是让转换功能成为你的类层次结构的一部分。

例如(非常骇人听闻)

class D;

class A
{
public:
    virtual D* GetDPtr() { return 0; }
};

class B : public virtual A
{
};

class C : public virtual A 
{
};

class D : public B, public C 
{
public:
    virtual D* GetDPtr() { return this; }
};
于 2010-07-27T21:07:17.160 回答
5

Android确实支持 RTTI。您需要最新的 NDK(至少 r5,最新的是 r6)并且您需要针对 GNU stdlibc++ 而不是默认值进行编译。

甚至在之前,CrystaX 的重建确实支持异常和 rtti(我们不得不使用它直到官方 NDK r5c,因为 r5a 和 r5b 有支持,但在旧的(2.3 之前的)系统上崩溃了)。

PS:真的应该禁止供应商在不支持异常和 rtti 时说他们支持 C++,因为大多数标准库,这是 C++ 标准的一部分,没有这两者都无法工作。再加上不支持它们是愚蠢的,尤其是对于异常,因为有异常的代码比没有异常的代码更有效(前提是它们被正确地用于发出异常情况的信号)。

于 2011-08-19T10:07:53.190 回答
3

在大多数情况下,访问者模式可用于避免向下转换。它也可以用来避免 dynamic_cast。

一些警告:

1) 必须可以更改违规类别。
2)您可能需要了解每个派生类。
3)必须知道对象至少从基类派生,您不能尝试强制转换完全不相关的类型。(这似乎应验了:“我想从基类向下转型到派生类”)

在以下示例中,我使用了模板。这些可以很容易地摆脱,但需要相当多的写作努力。

class A;
class B;
class C;
class D;

// completely abstract Visitor-baseclass.
// each visit-method must return whether it handled the object
class Visitor
{ 
public:
    virtual bool visit(A&) = 0;
    virtual bool visit(B&) = 0;
    virtual bool visit(C&) = 0;
    virtual bool visit(D&) = 0;
};

class A
{
public:
    virtual const char* func() { return "A"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class B : public virtual A
{
public:
    virtual const char* func() { return "B"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class C : public virtual A
{
public:
    virtual const char* func() { return "C"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class D : public B, public C
{
public:
    virtual const char* func() { return "D"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};

// implementation-superclass for visitors: 
// each visit-method is implemented and calls the visit-method with the parent-type(s)
class InheritanceVisitor : public Visitor
{ 
    virtual bool visit(A& a) { return false; }
    virtual bool visit(B& b) { return visit(static_cast<A&>(b)); }
    virtual bool visit(C& c) { return visit(static_cast<A&>(c)); }
    virtual bool visit(D& d) { return visit(static_cast<B&>(d)) || visit(static_cast<C&>(d)); }
};

template<typename T> // T must derive from A
class DerivedCastVisitor : public InheritanceVisitor
{
public:
    DerivedCastVisitor(T*& casted) : m_casted(casted) {}
    virtual bool visit(T& t) 
    { m_casted = &t; return true; }
private:
    T*& m_casted;
};

// If obj is derived from type T, then obj is casted to T* and returned. 
// Else NULL is returned.
template<typename T> 
T* derived_cast(A* obj)
{
  T* t = NULL;
  if (obj) 
  {
    DerivedCastVisitor<T> visitor(t);
    obj->accept(visitor);
  }
  return t;
}

int main(int argc, char** argv)
{
  std::auto_ptr<A> a(new A);
  std::auto_ptr<A> b(new B);
  std::auto_ptr<A> c(new C);
  std::auto_ptr<A> d(new D);

  assert(derived_cast<A>(a.get()) != NULL); // a has exact type A
  assert(derived_cast<B>(b.get()) != NULL); // b has exact type B
  assert(derived_cast<A>(b.get()) != NULL); // b is derived of A
  assert(derived_cast<C>(b.get()) == NULL); // b is not derived of C
  assert(derived_cast<D>(d.get()) != NULL); // d has exact type D
  assert(derived_cast<B>(d.get()) != NULL); // d is derived of B 
  assert(derived_cast<C>(d.get()) != NULL); // d is derived of C, too
  assert(derived_cast<D>(c.get()) == NULL); // c is not derived of D

  return 0;
}
于 2010-07-28T12:41:59.993 回答
1

编码:

template <typename E, typename T>
E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset = ...

对我来说效果不是很好,因为static const E* exact_obj它是零,所以静态const T& exact_obj_ref = *exact_objderefs 也为零,因此static const ptrdiff_t exact_offset也为零。

在我看来,派生类需要实例化(这可能是抽象类的问题......)。所以我的代码是:

template <typename D, typename B>
D & Cast2Derived(B & b)
{ static D d;
  static D * pD = & d;
  static B * pB = pD;
  static ptrdiff_t off = (char *) pB - (char *) pD;

  return * (D *) ((char *) & b - off);
} 

在 MSVC 2008、WinXP 32b 下测试。

欢迎任何意见/更好的解决方案。

葡聚糖

于 2011-08-19T09:37:59.623 回答
0

虚拟继承的问题在于基类地址不一定与派生地址相同。因此, even reinterpret_castor void*casts 将无济于事。

在不使用的情况下解决此问题的一种方法dynamic_cast是计算两种指针类型(确切类型和 ref 类型)之间的偏移量,以便在强制转换期间相应地修改对象地址。

 template <typename E, typename T>
 E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset =
     (const char*)(void*)(&exact_obj_ref)
     - (const char*)(void*)(exact_obj);
   return *(E*)((char*)(&ref) - exact_offset);
 }
于 2011-05-12T22:07:57.730 回答
-2

只要您有另一种方法来确保您正在做的事情是在运行时类型安全,只需使用 reinterpret_cast。

它与 C 风格转换基本相同,因此仅在必要时使用它,但它允许上面的代码编译。

于 2010-07-28T02:18:00.580 回答