C++ class 设计:协变

C++ class design: Covariance

问题

我想实现一些在图上工作的算法,return 节点对的分数表明这些节点是否相似。这些算法应该适用于单个节点对和所有可能的节点对。在后一种情况下,collection/matrix 应该是 returned.

我的方法

算法源自

class SimilarityAlgorithm {
public:
  Base(const Graph& G);

  virtual double run(node u, node v) = 0; // indices for nodes in the graph

  virtual ScoreCollection& runAll() = 0;
}

现在算法在内存使用上有所不同。某些算法可能是对称的,并且 (u, v) 和 (v, u) 的分数相同。这需要不同的 ScoreCollection 类型,应该 returned。一个例子是稀疏矩阵和三角矩阵,它们都来自 ScoreCollection.

这将归结为协变 return 类型:

class SpecificAlgorithm : SimilarityAlgorithm {
public:
  double run(node u, node v);

  // The specific algorithm is symmetric and thus uses a symmetric matrix to save memory
  SymmetricScoreCollection& runAll();
}

问题

您的设计似乎适合您描述的问题。

问题:

但是,您的 SpecificAlgorithm 有一个问题:runAll() 与基础 class 的虚函数 return 类型不同。因此它不会被调用(或者更可能的是,您的代码由于缺少虚函数而无法编译)。

解法:

也对 ScoreCollection 使用多态方法,使 SymmetricScoreCollection 成为 ScoreCollection 的派生 class:

class SymetricScoreCollection: public ScoreCollection {
//define the member functions to access the values virtual
...
}; 

class SpecificAlgorithm : public SimilarityAlgorithm {
public:
  double run(node u, node v);

  // The specific algorithm is symmetric and thus uses a symmetric matrix to save memory
  ScoreCollection& runAll();
};    

实际上它是factory method pattern的一个应用,具有以下作用:

  • SimilarityAlgorithm 是工厂,
  • SpecificAlgorithm 是具体的工厂
  • ScoreCollection 是产品
  • SymetricScoreCollection 是具体产品

补充说明:

runAll() 返回对 ScoreCollection 的引用会带来一些风险。假设 sa 是一个特定的算法。

在以下语句中:

ScoreCollection sc = sa.runAll();

sa.runAll() return 是对 SymetricScoreCollection 的引用,但它会将引用的对象复制到 sc,使其成为 ScoreCollection。 Slicing 发生,多态性将失效。

但是下面的语句会成功:

ScoreCollection& rsc = sa.runAll();

因为 rsc 是一个引用,它仍然会引用 sa.runAll()sa.runAll() 编辑的原始 SymetricScoreCollection 对象,并且一切都会按设计工作。

您会发现,在 returning 引用时很容易出现未注意到的错误。我建议 return 一个指针而不是一个引用。