Реализация сложного наследования в C ++ - PullRequest
3 голосов
/ 23 июня 2009

У меня есть следующие существующие классы:

class Gaussian {
public:
  virtual Vector get_mean() = 0;
  virtual Matrix get_covariance() = 0;
  virtual double calculate_likelihood(Vector &data) = 0;
};

class Diagonal_Gaussian : public Gaussian {
public:
  virtual Vector get_mean();
  virtual Matrix get_covariance();
  virtual double calculate_likelihood(Vector &data);
private:
  Vector m_mean;
  Vector m_covariance;
};

class FullCov_Gaussian : public Gaussian {
public:
  virtual Vector get_mean();
  virtual Matrix get_covariance();
  virtual double calculate_likelihood(Vector &data);
private:
  Vector m_mean;
  Matrix m_covariance;
};

Как видите, класс Gaussian действует как интерфейс, но не имеет никакой реализации. Это все работает нормально.

Теперь я хочу создать класс «AdaptedGaussian», в котором вектор данных, предоставленный для selected_likelihood, будет изменен до вычисления вероятности.

Некоторые требования:

  • AdaptedGaussian должен быть дочерним классом Gaussian
  • AdaptedGaussian должен уметь «оборачивать» или «быть экземпляром» каждого возможного гауссовского класса
  • AdaptedGaussian должен быть построен из уже существующего гауссовского объекта

Идея, которую я имею сейчас:

class Adapted_Gaussian : public Gaussian {
private:
  Gaussian* m_g;

public:
  virtual Vector get_mean() { return m_g->get_mean(); }
  virtual Matrix get_covariance() { return m_g->get_covariance(); }
  virtual double calculate_likelihood(Vector &data) 
  { 
    //do something with data
    return g->calculate_likelihood(Vector &data); 
  }
} 

Возможно, есть некоторые недостатки:

  • Для каждого метода (а их здесь больше, чем показано), в новый класс должен быть записан фиктивный метод
  • Если Гауссиан когда-либо расширяется, и этот класс будет забыт, могут появиться неприятные ошибки.

Я делаю это правильно? Или есть лучшие методы для реализации этого?

Может быть, есть хороший способ стандартизировать делегирование каждого не реализованного метода одноименному методу m_g?

Ответы [ 4 ]

5 голосов
/ 23 июня 2009

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

  1. Способ, которым библиотека классов Java решает проблему с фиктивным методом, заключается в создании фиктивного класса, который обеспечивает пустую реализацию для каждого метода. Все классы, которые не хотят реализовывать каждый отдельный метод, могут просто наследовать от этого фиктивного класса и выборочно переопределять методы, которые их интересуют.
  2. Если вы расширите свой класс Гаусса еще несколькими методами, если вы объявите их как чисто виртуальный метод, вы все равно получите ошибку компилятора в файле дочернего класса.
2 голосов
/ 23 июня 2009

Как вы указали, написание множества базовых сквозных функций утомительно и добавляет подразумеваемые накладные расходы на обслуживание. Кроме того, наличие члена указателя подразумевает дополнительные (хотя и простые) проблемы управления временем жизни принадлежащего указателя. Вероятно, самый простой способ решения этих проблем - сделать AdaptedGaussian шаблоном, адаптированным к конкретному экземпляру гауссовского языка для адаптации.

template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
{
    virtual double calculate_likelihood(Vector &data) 
    { 
        // do something with data
        return BaseGaussian::calculate_likelihood(Vector &data); 
    }
};

Это полагается на то, что все адаптированные экземпляры гауссианы являются конструктивными по умолчанию или, по крайней мере, соответствуют общей сигнатуре конструктора.

Если вы хотите создать AdaptedGaussian из существующего XXXGaussian, то, если XXXGaussian сам по себе является копируемым, вы можете добавить подходящий конструктор:

template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
{
public:
    AdaptedGaussian(const BaseGaussian& other) : BaseGaussian(other)
    {
    }
    // ...
};
1 голос
/ 23 июня 2009

Это также может быть решено с помощью паттерна стратегии .

Мне кажется, что Даффимо тоже думал в этом направлении с «композицией». Измените конструкцию таким образом, чтобы базовый класс вызывал некоторый метод другого объекта, который он содержит. Этот объект содержит код для Calculate_likelihood. Либо весь метод может быть отложен, либо только изменения (во втором случае по умолчанию будет просто ничего не делать).

Например: (исправленная версия)

class Gaussian {
   private:
      Cl_Strategy* m_cl_strategy;

   public:
      Gaussian(Cl_Strategy* cl_strategy) {
         m_cl_strategy = cl_strategy;
      };
      virtual Vector get_mean() = 0;
      virtual Matrix get_covariance() = 0;
      virtual double _calc_likelihood(Vector &data) = 0;
      virtual double calculate_likelihood(Vector &data) {
         m_cl_strategy->do_your_worst(this, data);
         return _calc_likelihood(data);
      };
};

Надеюсь, я правильно понял, мой C ++ немного запылен ...

_calc_likelihood должно быть реализовано подклассами, а Calculate_likelihood связывает все вместе.

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

0 голосов
/ 23 июня 2009

В Java обычно есть как интерфейс, так и абстрактный класс, который реализует его для обеспечения поведения по умолчанию для всех методов. (См. Дизайн Джошуа Блоха API-интерфейса Collections в пакете java.util.) Возможно, это также может помочь вам Вы предоставите клиентам возможность выбора либо интерфейса, либо абстрактного класса.

Вы также можете попробовать Композиция . Передайте экземпляр адаптированного гауссиана подклассам и отложите поведение до него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...