C ++ - 在不使用RTTI / dynamic_cast的情况下向下转换钻石形状的继承对象

时间:2010-07-27 20:52:54

标签: c++ casting multiple-inheritance rtti diamond-problem

我目前正致力于在非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 *'          从base到derived的转换需要dynamic_cast或static_cast

有什么想法吗?

6 个答案:

答案 0 :(得分:12)

您只能使用dynamic_cast进行此演员表;没有其他演员会这样做。

如果您无法设计界面以便不需要执行此类型的强制转换,那么您唯一能做的就是将强制转换功能作为类层次结构的一部分。

E.g。 (可怕的hacky)

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; }
};

答案 1 :(得分:5)

Android 支持支持RTTI。您需要最新的NDK(至少r5,最新版本是r6),您需要针对GNU stdlibc ++而不是默认编译。

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

PS:有人应该真的禁止供应商说他们支持C ++而不支持异常和rtti,因为大多数标准库,以及C ++标准的一部分,如果没有这两者,就无法工作。另外,不支持它们是愚蠢的,特别是对于异常,因为具有异常的代码比没有异常的代码更有效(假设它们适当地用于发出异常情况)。

答案 2 :(得分: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;
}

答案 3 :(得分: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_obj也会反映零,因此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下进行测试。

欢迎任何评论/更好的解决方案。

LUP

答案 4 :(得分:0)

虚拟继承的问题是基类地址不一定 与派生地址相同。因此,即使是reinterpret_castvoid*演员也会 没有帮助。

在不使用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);
 }

答案 5 :(得分:-2)

只要你有另一种方法可以确保你在做什么就是在运行时键入安全,只需使用reinterpret_cast。

它与C样式转换基本相同,所以只有在必要时才使用它,但它将允许上面的代码进行编译。