在C ++中实现复杂的继承

时间:2009-06-23 10:40:59

标签: c++ inheritance

我有以下现有课程:

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”类,其中在计算似然性之前,将改变提供给calculated_likelihood的数据向量。

一些要求:

  • AdaptedGaussian必须是高斯的儿童类
  • 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); 
  }
} 

可能存在一些缺点:

  • 对于每个方法(并且此处显示的内容多于此处),必须在新类
  • 中编写虚拟方法
  • 如果Gaussian被扩展,并且这个类会被遗忘,那么就会出现令人讨厌的错误。

我是以正确的方式做到这一点的吗?或者有更好的方法来实现这个吗?

是否有一种很好的方法可以将每个未实现的方法标准委托给m_g的相同命名方法?

4 个答案:

答案 0 :(得分:5)

看起来很好,我认为这是适配器模式的一个非常经典的实现。只是不要忘记为高斯类声明一个虚拟析构函数。至于缺点。

  1. Java类库处理虚方法问题的方法是创建一个为每个方法提供空实现的虚拟类。所有不想实现每个方法的类都可以从这个虚拟类继承,并有选择地覆盖它们感兴趣的方法。
  2. 如果使用更多方法扩展Gaussian类,只要将它们声明为纯虚方法,无论如何都会在子类文件中出现编译错误。

答案 1 :(得分:2)

正如您所指出的那样,编写大量基本的传递函数是繁琐的,并增加了隐含的维护开销。此外,具有指针成员意味着拥有指针的额外(尽管很简单)生命周期管理问题。解决这些问题的最简单方法可能是使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)
    {
    }
    // ...
};

答案 2 :(得分:1)

这也许可以通过Strategy Pattern来解决。

对我而言,duffymo也在朝这个方向思考“作曲”。以这种方式更改设计,基类调用它包含的其他对象的某些方法。该对象包含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一起绑定。

当然,这个解决方案会增加一些开销,但在某些情况下,开销可能没问题。

答案 3 :(得分:0)

在Java中,通常有一个接口和一个抽象类来实现它,以便为所有方法提供默认行为。 (请参阅Joshua Bloch在java.util包中设计的Collections API。)也许这也可以帮到你。您将为客户提供使用接口或抽象类的选择。

您也可以尝试composition。将一个改编的Gaussian实例传递给子类并将行为推迟到它。