Избегайте dynamic_cast при передаче базового класса в качестве параметра виртуальной функции.

Я не мог найти однозначного ответа на этот простой, но важный случай. (Буду признателен, если кто-нибудь направит меня к ответу).

Допустим, у меня есть два вида наследования: класс A, который является базовым классом класса B, и класс C, который является базовым классом класса D, и выполняю дополнительную работу (зная, что на самом деле это указатель на D).

Теперь предположим, что у класса A есть виртуальная функция foo, которая принимает в качестве параметра указатель на C. Чего я хотел бы добиться, так это того, что всякий раз, когда A вызывает «foo», он будет действовать так, как если бы он получил указатель на C, и всякий раз, когда B вызывает foo, он будет действовать так, как если бы получил указатель на D.

Компилятор позволил бы мне передать объект, который был инициализирован как: C* c = new D();, но внутри 'foo', насколько я могу судить, нет никакого способа сказать, что на самом деле мы получили указатель на D без использования dynamic_cast.

Вот набросок того, о чем я думаю:

class C {};
class D : C {// has more functions };


class A {

public:
    virtual void foo (C* c) { //do something with c* };

};


class B : A {

public

    virtual void foo (C* c) { // call A::foo()
                              //do something with c knowing that c* as actually a D*
                             }

};

Использование dynamic_cast внутри B::foo кажется неизбежным для достижения того, чего я хочу. Я знаю, что вообще говоря, использование dynamic_cast предполагает плохой дизайн ООП, поскольку он может нарушить LSP, но я не могу придумать других способов добиться того, чего я хочу.

Я хотел бы услышать некоторые предложения - может быть, как бы изменить дизайн, или даже "динамический_каст необходим" будет в порядке.

Надеюсь я понятно изложил свою мысль, если нет - уточню свой вопрос.

Обновление: возможно, я должен был упомянуть об этом раньше (и, кстати, я не просто придумываю это, мне действительно приходилось программировать что-то подобное в университете), но идея о том, что пятый класс, скажем, E будет содержать массив ( или список или что-то еще) типа A, и у меня есть алгоритм, который выполняет итерацию по этому массиву, и для каждого элемента он должен вызывать этот метод «foo» - где, если класс на самом деле был A *, он должен работать как раньше, и если это B *, он должен выполнить «foo» класса B.

Что-то вроде этого:

//within class E
A** arrayOfAs; //(and B's)
for (int i = 0 ; i < lengthOfArray ; ++i ) 
{
    arrayOfAs[i]->foo(/*someObject*/);
}

Спасибо!


person Alonbs    schedule 31.03.2014    source источник
comment
Вы понимаете, что используете частное наследование? Здесь не может быть ЛСП.   -  person juanchopanza    schedule 31.03.2014
comment
'Избегайте dynamic_cast при передаче базового класса...' Выбирайте дизайн получше, вопрос уже пахнет...   -  person πάντα ῥεῖ    schedule 31.03.2014


Ответы (1)


Чтобы делать то, что вы хотите, вам понадобится dynamic_cast в B::foo. Но... если вы можете вызывать B::foo только с D, функция не должна перегружать A::foo. Это нарушает LSP и более или менее противоречит цели объектно-ориентированного кода.

person James Kanze    schedule 31.03.2014