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