类继承层次结构设计问题

a class inheritance hierarchy design issue

本文关键字:问题 结构设计 层次 继承      更新时间:2023-10-16

我有以下图形的类层次结构:

typedef vector<int> ArrayI;
typedef vector<Array<long>> Mat2DB;
typedef vector<ArrayI> adjList;
class baseGraph {
    int nodes;
    ArrayI degree;
    //some member functions.
}
class matGraph: public baseGraph {
    Mat2DB matrix;
    //member functions.
}
class lMatGraph: public matGraph {
    ArrayI labels;
    //member functions.
}
class listGraph: public baseGraph {
    adjList list;
    //member functions.
}
class lListGraph: public listGraph {
    ArrayI labels;
    //member functions.
}

现在在这个类中,我有许多其他函数,大部分是虚拟的,这样当我在使用基类指针的同时调用正确的函数时。

例如,我有一个函数sssp(int node),它实现了单源最短路径。CCD_ 2和CCD_ 3分别是图的邻接矩阵表示和邻接列表表示。现在不需要更改这些图的标记版本的定义,所以我不需要在lListGraphlMatGraph 中再次定义这些函数

现在我遇到的唯一问题是lListGraphlMatGraph类中的setLabel(const ArratI &)。我需要这个函数是虚拟的,这样它就可以通过基类指针来调用,但同时我没有任何东西,比如类matGraphlistGraph的标签。

我不知道我的设计层次结构是否正确,但它对我来说似乎很直观。所以任何关于它的评论都是好的。我能用setLabel函数做什么。有这样一个函数可以吗(对我来说,这看起来像是一种变通方法,所以这个问题)或者我需要重新考虑我的类层次结构。

附言:如果有一些书可以让我练习这样的设计问题,我也会很高兴的。我碰到这些熟食店,不知道该怎么办。

编辑:

类图的使用用于另一个类clustering,其中我有一个成员baseGraph *graph,即

class clustering {
    baseGraph *graph;
}

我将指向基类的指针存储在这里,这样我就可以使用class graph中的不同算法(实现为函数)。对于集群类,它再次取决于我想要使用什么类型的图。

也许是这样?

typedef vector<int> ArrayI;
typedef vector<Array<long>> Mat2DB;
typedef vector<ArrayI> adjList;
class baseGraph {
    int nodes;
    ArrayI degree;
    virtual void sssp(int node);
    //some member functions.
}
class labeledGraph: public virtual baseGraph {
    ArrayI labels;
    virtual void setLabel(const ArratI &);
    //member functions.
}
class matGraph: public virtual baseGraph {
    Mat2DB matrix;
    //member functions.
}
class lMatGraph: public virtual matGraph, public virtual labeledGraph {
    //member functions.
}
class listGraph: public virtual baseGraph {
    adjList list;
    //member functions.
}
class lListGraph: public virtual listGraph, public virtual labeledGraph {
    //member functions.
}

我在这里假设,当你们本应该从baseGraph(typeo)继承时,你们却错误地从图继承了——即使不是,它也会归结为同一点。

还有粗略的编码,如果你有问题或有错误,请随时询问。

您说setLabel应该通过基类指针调用,所以这必然意味着它应该在基类中声明,即使这没有意义。您可以通过两种可能的方式为未标记的图实现setLabel

  • 什么都不做-只需忽略设置标签的请求
  • 抛出一个异常(例如abort)-可能出了问题,所以用户应该知道

每种方法都是一种变通方法,所以您应该考虑为什么应该通过基类指针调用setLabel,并可能更改此决定。我希望,如果你的算法真的需要一个带标签的图,可以使用适当的类型而不是基类类型,这样你就不需要对基类进行任何攻击。

请注意,如果你不断地向基类中添加与每个派生类相对应的东西,那么你最终会在基类中遇到很多麻烦——这不好!


此外,以下内容可能会解决您使用setLabel的问题,并使您的类层次结构"更健康"。

考虑将class matGraph0等基本算法从类声明中移除,使它们重载独立函数,而不是成员函数。通过这种方式,您也不需要在基类中声明sssp。如果你采用这个准则,当你实现一个新的算法时,编译器会检查所有的函数调用,如果缺少一个,就会发出错误(这比崩溃或得到错误的结果要好)。

class baseGraph {
    int nodes;
    ArrayI degree;
    // a minimum number of member functions (e.g. getNode; getEdges)
}
class matGraph: public graph {
    Mat2DB matrix;
}
class lMatGraph: public matGraph {
    ArrayI labels;
    void setLabel(const ArrayI &);
}
int sssp(const matGraph& graph, int node)
{
    // Some code
}
int sssp(const lMatGraph& graph, int node)
{
    // Some code; here you can use labels
}

这一点在《有效C++》一书中进行了讨论(有效C++第23项:与成员函数相比,更喜欢非成员非好友函数)

这一切都归结为一个简单的选择。如果我试图在图中设置一个实际上不支持标签的标签,会发生什么?

  1. 什么都没有(尝试可能会被记录,但会被忽略)
  2. 灾难性的失败
  3. 我甚至不能尝试(编译器不应该让我)

就是这样。这些都是你的选择。

前两个选项很简单,只需编写一个报告错误(记录错误或引发异常)的虚拟函数。

第三个很有趣。这意味着根本没有相应的虚拟功能。不在你的类中,也不在任何基类中。这违背了你的设计,但你的设计并不一定完美。

那么你该如何设定一个标签呢?通过一些不是指向基类的指针:)它可以是指向另一个基类的指针(mixin——您使用多重继承为图添加标记功能)。或者,您可以将设计模板化,这样层次结构就不会真正重要,并且您总是静态地知道对象的派生类型最多。