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 一个指针而不是一个引用。
问题
我想实现一些在图上工作的算法,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 一个指针而不是一个引用。