我目前正致力于在非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
有什么想法吗?
答案 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_cast
或void*
演员也会
没有帮助。
在不使用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样式转换基本相同,所以只有在必要时才使用它,但它将允许上面的代码进行编译。