纯虚函数 vs 虚函数?

5

我正在开发一个类,其中包含一些功能,我认为将来可能需要扩展,但现在不需要。如果这个类被扩展,那么我认为实例化基类就毫无意义了。

例如,假设我的基类是一棵树。一种方法是将树所需的所有内容都放入树类中,并让它保持原样。然而,这棵树以后可能在程序的其他方面有用,因此我考虑创建一个纯虚拟的 onNodeVisited 函数。派生类可以实现自己的版本的 onNodeVisited,而不必担心基类中定义的树遍历的具体细节。

不使用纯虚函数并将树功能和应用程序特定功能保留在一个类中(虚拟 onNodeVisited)是否有意义?还是说,我应该使树类抽象化,并为应用程序特定部分实现一个子类。

3个回答

2

我现在不会做出决定。如果有必要,稍后可以为Tree创建一个抽象基类,并将代码移到那里。

另一种继承的选择是使用指向函数或函数对象类型的指针进行调用。这样更容易重用,因为您不必为每种新情况都创建新类。


1

我可以看到两个选择,它们对我来说都是合理的。

如果你只是在考虑可能会有用的事情,那么我会选择YAGNI路线,完全不使用它,直到你真正需要它为止。

如果你非常确定你真的需要它,只是还没有编写足够的其他代码来使用它,那么将其设计到类中是值得的,即使你还没有使用它。

但这并不像一个纯虚函数的好情况。纯虚函数意味着1)包含纯虚函数的类不能直接实例化——它只能用作基类,2)为了能够创建派生类的对象,它们必须提供纯虚函数的实现。

换句话说,纯虚函数几乎表示与你的情况相反。在对象存在之前,必须覆盖纯虚函数。你最多只是构建了一个“钩子”,以便未来特定的扩展更容易实现。

我会重复我的建议:除非你非常确定这将被使用,最好只设计你需要的内容,并在此基础上停止。如果你决定需要(或者真的非常想要)包含它,我建议尽量保持松耦合。显而易见的方法是使用访问者模式。这定义了一个单独的访问者类来处理访问和处理节点。树节点类将包括一个额外的函数(传统上命名为accept),它接受一个参数:指向访问者对象的指针(或引用)。访问者类通常是一个抽象基类,具有一个成员函数(传统上命名为visit)来对节点进行处理。这通常是一个抽象基类。

然后,当您决定每个节点上真正要执行的处理时,您可以从visitor派生一个新类,覆盖visit以执行每个节点的处理。树根本不需要修改。这是一个众所周知的模式(即使在C++程序员中也是如此,他们通常对其他模式不太“友好”),因此假设您使用正常的名称,大多数人都会很快和容易地识别它。代码本身通常也非常简单--accept通常看起来像这样:

struct tree_node;

class visitor { 
    virtual void visit(tree_node *node) = 0;
};

struct tree_node { 
    void accept(visitor *v) { v->visit(this); }

    // ...
};

接下来,为了添加处理,您可以从访问者类中派生出一个新的类,并重写visit方法以进行所需的处理。树形结构方面不需要做任何修改。这也有一个好处,就是一个访问者类可以被编写用于访问多种类型的节点(即使这些节点不是通过继承相关联的)。


1

显然,这必须根据具体情况来决定,对于树的例子而言,一个关键的决策/问题是是否所有使用该树的用户都需要/想要实现onNodeVisited函数。如果该树可以与界面的其他部分一样使用(例如,它支持get_next_child()风格的迭代,或者某些“路径”查找),那么看起来树可能对那些从不打算访问每个节点的人有用,因此他们不想实现onNodeVisited函数。在这种情况下,onNodeVisited就不应该是纯虚函数,现在或将来都不应该是。如果您的设计决策是使树的接口如此受限,以至于没有访问就无法使用该类,则可以通过将其设置为纯虚函数来坚持要求人们实现onNodeVisited


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接