在 C++ 中实现复杂的继承
Posted
技术标签:
【中文标题】在 C++ 中实现复杂的继承【英文标题】:Implementing complex inheritance in C++ 【发布时间】:2009-06-23 10:40:59 【问题描述】:我有以下现有课程:
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”类,其中提供给计算似然的数据向量将在计算似然度之前发生变化。
一些要求:
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);
可能有一些缺点:
对于每个方法(这里显示的不止一个),必须在新类中编写一个虚拟方法 如果 Gaussian 被扩展,而这个类会被遗忘,就会出现令人讨厌的错误。我这样做的方式是否正确?还是有更好的方法来实现这一点?
是否有一种好方法可以标准地将每个未实现的方法委托给 m_g 的同一个命名方法?
【问题讨论】:
我在这里不明白的是:你真的在 calculate_likelihood 中调用“get_covariance”还是这只是一个错误,你想调用 calculate_likelihood?我必须承认,我的统计数据非常生疏...... 哎,那是个错误。谢谢,我更正了。 顺便说一句。 calculate_likelihood 真的无效吗?别这么想!! 你又是对的。对不起,我的马虎。 【参考方案1】:看起来不错,我认为这是适配器模式的一个非常经典的实现。只是不要忘记为您的高斯类声明一个虚拟析构函数。至于缺点。
-
Java 类库处理伪方法问题的方式是创建一个伪类,为每个方法提供空实现。所有不想实现每个方法的类都可以从这个虚拟类继承,并有选择地覆盖他们感兴趣的方法。
如果您使用更多方法扩展 Gaussian 类,只要将它们声明为纯虚拟方法,您的子类文件中就会出现编译器错误。
【讨论】:
【参考方案2】:正如您所指出的,编写大量基本的传递函数很乏味,并且会增加隐含的维护开销。此外,拥有指针成员意味着拥有指针的额外(尽管简单)生命周期管理问题。解决这些问题的最简单方法可能是使 AdaptedGaussian 成为模板,以要适应的 Gaussian 特定实例为模板。
template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
virtual double calculate_likelihood(Vector &data)
// do something with data
return BaseGaussian::calculate_likelihood(Vector &data);
;
这确实依赖于所有适用的 Gaussian 实例都是默认可构造的,或者至少符合通用构造函数签名。
如果你想从现有的XXXGaussian
构造一个AdaptedGaussian
,那么只要XXXGaussian
本身是可复制的,你就可以添加一个合适的构造函数:
template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
public:
AdaptedGaussian(const BaseGaussian& other) : BaseGaussian(other)
// ...
;
【讨论】:
嗯,当我只有一个指向现有高斯的指针时,我将如何构造一个 AdaptedGaussian? 也许我理解错了,在这个解决方案中,AdaptedGaussian 是适应的特定高斯类型的一个实例。如果你想要一个适应的对角高斯,你可以创建一个 AdaptedGaussian这也可以通过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 绑定在一起。
当然,这种解决方案会增加一点开销,但在某些情况下,开销可能还可以。
【讨论】:
这个想法并不完全奏效。每个子类都有自己的 calculate_likelihood 实现,因此没有适应的数据。除此之外,我希望完全在当前课程之外拥有此功能。这是因为此功能可能以不同的方式/方法实现,并且仅在少数应用程序运行中使用。 感谢您的反馈。当然,我也可以尽可能多地暗示,因为我理解这个问题。这就是我写“也许”的原因。从您的代码 sn-ps 中,整个上下文并不那么清楚。当每个子类都有自己的calculate_likelihood实现时,你的例子怎么能工作——因为它也对它做了一些限制?? 我不完全明白为什么我的示例不起作用。因为calculate_likelihood在Gaussian中是虚的(并没有实现,见= 0),所以会执行真实对象的calculate_likelihood函数。 Gaussian* 指针始终指向 Gaussian 子类的实例。 查看我对问题的评论。【参考方案4】:在 Java 中,通常同时拥有一个接口和一个实现它的抽象类,以便为所有方法提供默认行为。 (请参阅 Joshua Bloch 在 java.util 包中对 Collections API 的设计。)也许这对您也有帮助。您将为客户提供使用接口或抽象类的选择。
你也可以试试composition。将适应的高斯实例传递给子类并将行为推迟到它。
【讨论】:
以上是关于在 C++ 中实现复杂的继承的主要内容,如果未能解决你的问题,请参考以下文章