C++ - downcasting ромбовидная форма наследованный объект без RTTI/dynamic_cast

Я в настоящее время работаю над интеграцией стороннего пакета, который использует много материала RTTI на платформе не-RTTI (Android). В основном я сделал свою собственную реализацию 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: не может преобразовать '*' к 'D*'; преобразование из виртуального базового класса подразумевается

ошибка C2440: 'инициализация': не может преобразовать из 'test_convert:: *' к 'test_convert::D *' Бросок от основы до полученного требует dynamic_cast или static_cast

Какие-либо идеи?

14
задан log0 12 May 2011 в 22:47
поделиться

3 ответа

Это приведение можно выполнить только с помощью 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; }
};
12
ответ дан 1 December 2019 в 13:21
поделиться

В большинстве случаев шаблон посетителя можно использовать, чтобы избежать отталкивания. Его также можно использовать, чтобы избежать 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 December 2019 в 13:21
поделиться

Если у вас есть другой способ убедиться, что то, что вы делаете, безопасно по типу во время выполнения, просто используйте reinterpret_cast.

По сути, это то же самое, что и приведение в стиле C, поэтому используйте его только в случае необходимости, но это позволит скомпилировать приведенный выше код.

-2
ответ дан 1 December 2019 в 13:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: