приведение указателя базового класса к одному из нескольких возможных указателей производного типа на основе условия - PullRequest
1 голос
/ 05 января 2012

У меня есть базовый класс B и несколько производных шаблонных классов D<int>, D<float>, D<double> и т. Д. (Более десяти)

В моей программе я нахожу ситуацию, когда у меня есть указатель B, который я ЗНАЮ, указывает на экземпляр одной из специализаций D. У меня также есть уникальный ключ, который идентифицирует производный тип.

Итак, я хочу вызвать правильный метод производного класса, используя мой указатель базового класса и ключ уникального типа. Я на самом деле хочу сделать это в нескольких местах, поэтому единственное решение, которое я нашел, не только некрасиво, но и громоздко.

Есть ли лучший способ получить доступ к членам производного класса, имеющим базовый указатель и уникальный ключ в C ++?

Я не хочу / не могу изменить базовый класс. буст библиотека это честная игра.

Вот мой потенциальный код, но я не доволен, повторяя это везде, где мне нужно получить доступ к производной функции / переменной-члену. Все это для одного вызова функции-члена? !!

B * bptr = ...
std::type_info * typekey = ...

if        (typekey == &typeid(D<float>) ) {
    D<float> * dptr = static_cast<D<float>*>(bptr);
    dptr->derivedMember();
} else if (typekey == &typeid(D<double>) ) {
    D<float> * dptr = static_cast<D<double>*>(bptr);
    dptr->derivedMember();
} else if (typekey == &typeid(D<int>) ) {
    D<float> * dptr = static_cast<D<int>*>(bptr);
    dptr->derivedMember();
} 

Ответы [ 3 ]

2 голосов
/ 05 января 2012

Если все методы D<type>:: имеют одинаковое имя DerivedMember, и я предполагаю, что базовый класс B не объявляет это как виртуальное, вы можете изменить свою иерархию с:

class B { etc. };

template<class T>
class D : public B
{
};

на:

class B { etc. };

class C : public B
{
 virtual void derivedMember() = 0;
};


template<class T>
class D : public C
{
public:
void derivedMember() { etc. };
};

Тогда вы можете иметь:

void f(C* c) /* or take a B* and do: C* c = static_cast<C*>(b); */
{    
  c->derivedMember();
}
0 голосов
/ 05 января 2012

Я думаю, что использование dynamic_cast может решить проблему:

    B* base
    if(Class1* c1 = dynamic_cast<Class1*>(base))
    // it's a Class1*
    else if (Class2* c2 = dynamic_cast<Class2*>(base))
    // it's a Class2*
0 голосов
/ 05 января 2012

Как отмечается в комментариях, правильный способ справиться с ситуацией - ввести метод virtual и заставить C ++ правильно распределять вызов.

Поскольку вы не можете изменить базовый класс, вы можете вставить новый класс с помощью виртуальной функции и вместо этого извлечь все классы из нового класса.

Если вам нужно повторить это поведение со многими функциями, возможно, еще не определенными и предоставленными клиентским кодом, вы можете использовать шаблон Visitor . Фактически, если вы не можете изменить базовый класс, вы можете, по крайней мере, использовать технику, подобную Vistior, чтобы хранить утомительные проверки на клавиатуре в одном месте и извлекать фактические операции в отдельные классы. Это избавит вас от необходимости переписывать проверки на клавиатуре в случае использования другой derivedMember -подобной функции.

...