Метод переопределения для семейства подклассов - PullRequest
5 голосов
/ 15 мая 2011

Учитывая устаревший код, система имеет следующую иерархию классов:

          Base
          ^
          |
----------+---------------
^      ^     ^     ^     ^ 
|      |     |     |     |
A1     B1    C1    D1    E1
^      ^     ^     ^     ^ 
|      |     |     |     |
A2     B2    C2    D2    E2
.......
^      ^     ^     ^     ^ 
|      |     |     |     |
An     Bn    Cn    Dn    En

Иерархия представляет сообщения в некотором конкретном домене.

Базовый класс - это, конечно, базовый класс всех сообщений.A1..E1 - это сообщения, принадлежащие версии 1 домена, A2..E2 - версии 2 и т. Д.Обратите внимание, что An должен наследоваться напрямую от An-1, поскольку An переопределяет определенные методы An-1.

Существует некоторая функциональность, общая для всех сообщений, поэтому она определяется как Base :: PerformFunctionality .Некоторая часть функциональности относится только к версии n , поэтому есть виртуальная функция Base :: SpecificPartOfFunctionality , которая вызывается Base :: PerformFunctionality .

Так что моя проблема в том, как переопределить Base :: SpecificPartOfFunctionality всеми An..En.

Я вижу 2 возможных решения, которые мне не очень нравятся:

  1. Реализация Base :: SpecificPartOfFunctionality в каждом и каждом An..En.

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

    Дополнительная проблема состоит в том, что, если вводится новый класс Fn, разработчик может забыть реализовать SpecificPartOfFunctionality .

  2. Ввести BaseN класс, производный от Base, в то время как каждый An..En также является производным от BaseN :

    class BaseN : public Base {
       //...
       SpecificPartOfFunctionality() { ...}
    };
    
    class An: public An-1, public BaseN { .. }
    

    Проблема с этим решением состоит в том, что оно создает проблему diamond .

    Дополнительная проблема заключается в том, что произойдет, если какой-либо другой версии m потребуется переопределить Base :: SpecificPartOfFunctionality .Следуя решению, мы введем BaseM , который переопределит Base :: SpecificPartOfFunctionality .Так что SpecificPartOfFunctionality будет вызываться для - из BaseN или BaseN .Это полный беспорядок.

Есть предложения?

Ответы [ 2 ]

3 голосов
/ 15 мая 2011
struct Base {
  virtual void PerformFunctionality() {
    stuff();
    SpecificPartOfFunctionality();
    more_stuff();
  }
private:
  virtual void SpecificPartOfFunctionality() {}
};

template<class Prev>
struct Version3 : Prev {
protected:  // Not private if v4's override might need to call this.
  virtual void SpecificPartOfFunctionality() {
    v3_specific_stuff();
  }
};

struct A1 : Base {};
struct A2 : A1 {};
struct A3 : Version3<A2> {};

struct B1 : Base {};
struct B2 : B1 {};
struct B3 : Version3<B2> {};

Единственный недостаток - вы не можете легко пересылать конструкторы в текущем C ++ и, вероятно, всегда будете использовать конструктор по умолчанию для A2 и B2 просто для простоты.

Однако в C ++ 0x вы можете пересылать конструкторы:

template<class Prev>
struct Version3 : Prev {
  using Prev::Prev;
  //...
};

struct C2 : C1 {
  C2(int);  // No default constructor.
};
struct C3 : Version3<C2> {
  C3() : Version3<C2>(42) {}
};

Обратите внимание, что An должен наследоваться напрямую от An-1, поскольку An переопределяет определенные методы An-1.

Вы что-то не так поняли. An не должен наследовать напрямую от An-1. Например, это работает просто отлично:

struct Example {
  virtual void f();
};

template<class B>
struct Intermediate : B {};

struct Concrete : Intermediate<Example> {
  virtual void f();  // Overrides Example::f.
};

Помните, что если это не не сработало, то ваш An не может переопределить SpecificPartOfFunctionality из вашего базового класса! An не наследуется напрямую от Base ни в одном из ваших примеров.

1 голос
/ 15 мая 2011

Определите функцию друга в глобальной области видимости, которая реализует эту функциональность, и пусть SpecificPartsOfFunctionality ничего не делает, кроме как вызывает ее.Например,

void FunctionalityN(Base* b)
{
    b->DoThings();
    ...
}

class An : public Base
{
    friend void FunctionalityN();
    virtual void SpecificPartOfFunctionality() { FunctionalityN(this); }
    ....
};

Строго говоря, ФункциональностьN даже не обязательно быть другом, хотя это облегчит задачу.Это упрощает расширение до Fn, Gn и т. Д. С последующей заменой FunctionalityN на NewFunctionalityN тоже довольно просто.

...