полиморфизм — идиома для определенного рефакторинга в переполнении стека

Я задал этот вопрос:

Лучший способ реорганизовать иерархию классов

так плохо, что я был вынужден принять совершенно правильный ответ. Моя проблема заключается в следующем:

У меня есть класс CGrandMother публичный метод virtual bool Compute() это делает

virtual bool Compute()
{
return false;
}

От CGrandMother получает публично CMother который не реализует Compute, Теперь из CMother выводить публично C1 а также C2 которые реализуют virtual bool Compute(), Сейчас virtual bool C1::Compute() а также virtual bool C2::Compute() соответственно делать много вещей, соответствующих соответственно C1 и к C2, но также много идентичных вещей, соответствующих CMother, Теперь есть класс CFamily имеющий в качестве члена указатель на CMother и почти везде в коде Compute вызывается через строки вида

ptrCMother->Compute();

Я хочу выделить общие вещи, связанные с CMother Завершен в C1 а также C2 чтобы мне не пришлось менять все эти ptrCMother->Compute();, Конечно, я могу создать функцию-член в CMother делать это, и позвонить последнему в bool C1::Compute() а также bool C2::Compute(), Но…

В c++, если у вас есть B вытекающий из A, A::dostuff() а также B::dostuff() и если p указывает на тип B затем p->dostuff() выполнит B::dostuff() по полиморфизму. Я хотел бы знать, есть ли идиома / шаблон, позволяющий мне достичь этого: «p->dostuff()«выполнит»A::dostuff()«или нет (в соответствии с bool скажем) а потомB::dostuff()«, что, конечно, не происходит в c++ для функций-членов класса, которые не являются конструкторами и т. д.

Для ясности: базовый метод не вызывается до того, как его соответствующий производный метод будет вызван косвенным путем. Существует ли идиома / шаблон, позволяющий вызывать базовый метод (или нет, согласно bool) до его соответствующего производного метода?

2

Решение

Что не так с упомянутым вами решением, состоящим в создании метода в классе CMother, который вызывается его дочерними элементами?

#include <iostream>
#include <memory>
using namespace std;

class CGrandMother
{
private:
virtual bool _Compute()
{
return false;
}
public:

bool Compute()
{
return _Compute();
}
};

class CMother : public CGrandMother
{
protected:
void _CommonStuff()
{
cout << "work common to children" << endl;
}
};

class C1 : public CMother
{
private:
virtual bool _Compute() override
{
_CommonStuff();
cout << "work specific to C1" << endl;

return true;
}
};

class C2 : public CMother
{
private:
virtual bool _Compute() override
{
_CommonStuff();
cout << "work specific to C2" << endl;

return true;
}
};

int main() {

unique_ptr<CMother> c1(new C1);
unique_ptr<CMother> c2(new C2);

c1->Compute();
c2->Compute();

return 0;
}

Обратите внимание, что я ограничил модификаторы доступа моих классов:

  • Нет общедоступной виртуальной функции, следуя шаблону Non-Virtual Interface. Функция, как общедоступная, так и виртуальная, выполняет две задачи: она определяет интерфейс и реализацию и часто оказывается плохой в обоих случаях.
  • Метод _CommonStuff () защищен, поэтому он невидим для остального мира, но все еще может вызываться подклассами

Кроме того, вы заставляете CMother публично наследовать от CGrandMother, что означает, что CMother является CGrandMother. Это действительно так?

Также обратите внимание, что если CMother был главой иерархии, то вы могли бы вызвать _CommonStuff () через его открытый (но не виртуальный!) Метод Compute, и все подклассы также автоматически вызовут _CommonStuff (). Это одна из замечательных особенностей этого шаблона: вы можете легко заставить свой метод Compute () работать, который является общим для всех подклассов, проверять инварианты и т. Д.

Это будет выглядеть так: https://ideone.com/vXd4fL

Я удалил CGrandMother, и теперь CMother является главой моей иерархии. В своем открытом методе Compute () я вызываю _CommonStuff (). Детям нужно только реализовать _Compute (). _CommonStuff () будет вызываться автоматически всеми дочерними элементами, когда они вызывают Compute ().

0

Другие решения

Других решений пока нет …