简体   繁体   English

纯虚函数与虚函数?

[英]Pure virtual function vs. virtual function?

I'm developing a class with some functionality that I think might need to be extended later, but not now. 我正在开发一个具有某些功能的类,我认为可能需要稍后扩展,但现在不能。 If the class were to be extended, then I think this would make instantiating the base class meaningless. 如果要扩展类,那么我认为这会使实例化基类毫无意义。

For example, let's say my base class is a tree. 例如,假设我的基类是树。 One approach would be to just put everything that the tree needs to do for my purposes in the tree class and leave it at that. 一种方法是在树类中放置树需要做的所有事情,并将其留在那里。 However, this tree might be useful in other aspects of the program later on in life, so I've thought about creating a pure virtual onNodeVisited function. 但是,这个树在以后的生活中可能对程序的其他方面很有用,所以我考虑过创建一个纯虚拟onNodeVisited函数。 Derived classes could then implement their own version of onNodeVisited , and not have to worry about the specifics of the tree traversal defined in the based class. 然后,派生类可以实现自己的onNodeVisited版本,而不必担心基类中定义的树遍历的细节。

Does it make sense to not use a pure virtual function and keep the tree functionality and application-specific functionality in one class (virtual onNodeVisited )? 不使用纯虚函数并将树功能和特定于应用程序的功能保存在一个类(virtual onNodeVisited )中是否onNodeVisited Or, should I make the tree class abstract and implement one sub-class for the application-specific part. 或者,我应该使树类抽象并为特定于应用程序的部分实现一个子类。

I wouldn't decide that now. 我现在不会这样做。 Later you could make an abstract base class for Tree and move code there, if that made sense. 稍后你可以为Tree创建一个抽象基类并在那里移动代码,如果这有意义的话。

Another option to inheritance for this kind of thing is a pointer-to-function or functor type to call. 继承这种事物的另一个选择是要调用的函数指针或函数类型。 It's a lot easier to reuse, because you don't have to keep making new classes for each new situation. 重用更容易,因为您不必为每种新情况继续创建新类。

Obviously this has to be decided on a case by case basis, and for the tree example it would seem that a crucial decision/question is whether all users of the tree would need/want to implement the onNodeVisited function. 显然,这必须根据具体情况来决定,对于树的例子,似乎关键的决定/问题是树的所有用户是否需要/想要实现onNodeVisited函数。 If the tree can equally be used with the other parts of the interface (eg it supports get_next_child() -style iteration, or some "path" lookup), then it sounds like a tree could be useful to people who never intend to visit each node, and would therefore not want to implement an onNodeVisited function. 如果树可以同样地与接口的其他部分一起使用(例如,它支持get_next_child()样式迭代,或者某些“路径”查找),那么听起来树对于从不打算访问每个部分的人来说可能是有用的。节点,因此不希望实现onNodeVisited函数。 In that case, onNodeVisited should not be pure virtual, now or ever. 在这种情况下, onNodeVisited不应该是纯虚拟的,现在或永远。 If your design decision is to have the tree's interface so restrictive that the class is useless without visitation, then you might insist that people implement onNodeVisited by making it pure virtual. 如果你的设计决定是让树的界面如此限制,以至于没有访问时类没用,那么你可能会坚持让人们通过将它onNodeVisited纯虚拟来实现onNodeVisited

I can see two choices that strike me as reasonably sensible. 我可以看到两个选择让我觉得合情合理。

If you're just thinking in terms of things that might eventually become useful, then I'd go the YAGNI route, and leave it out completely until or unless you find a real need for it. 如果你只是考虑可能最终会变得有用的东西,那么我会选择YAGNI路线,并将其完全排除,直到或除非你发现它真正需要它。

If you're quite certain you're really going to need it, and just don't have enough of the other code written to put it to use yet , then it's worth designing into the class, even though you aren't using it yet. 如果你肯定你真的需要它,只是没有足够的写入把它尚未使用其他代码,那么它的价值设计到类,即使你不使用它然而。

This does not sound like a good situation for a pure virtual function though. 虽然这对于纯虚函数来说听起来不是一个好的情况。 A pure virtual function means 1) the class containing the pure virtual function cannot be instantiated directly -- it can only be used as a base class, and 2) to be able to create objects of a derived class, they must provide an implementation of the pure virtual function . 纯虚函数意味着1)包含纯虚函数的类不能直接实例化 - 它只能用作基类,2)能够创建派生类的对象,它们必须提供实现纯虚函数。

In other words, a pure virtual function would indicate almost the opposite of your situation. 换句话说,纯虚函数表示几乎与您的情况相反。 A pure virtual function must be overridden before objects of the class can exist at all. 必须在类的对象完全存在之前覆盖纯虚函数。 You're (at most) building in a "hook" to make a specific future expansion easy. 你(最多)建立一个“钩子”,使特定的未来扩展变得容易。

I'll repeat my advice above: unless you're quite certain this will be used, it's probably best to just design in what you do need, and leave it at that. 我会重复我的建议上面:除非你是这使用相当肯定,它可能是最好只在需要什么样的设计,并留在这一点。 If you do decide you need (or really, really want) to include it, I'd try to keep it as loosely coupled as reasonable. 如果决定你需要(或真的,真的希望)包括它,我会尽量保持它作为松耦合合理。 The obvious route to that would be to use the visitor pattern. 显而易见的途径是使用访问者模式。 This defines a separate visitor class that handles visiting and processing nodes. 这定义了一个处理访问和处理节点的单独访问者类。 The tree node class would include an extra function (traditionally named accept ) that takes one parameter: a pointer (or reference) to a visitor object. 树节点类将包括一个额外的函数(传统上称为accept ),它接受一个参数:一个访问者对象的指针(或引用)。 The visitor class is a typically-abstract base class with a member function (traditionally named visit ) for doing processing on a node. 访问者类是一个典型的抽象基类,具有用于在节点上进行处理的成员函数(传统上称为visit )。 This typically is an abstract base class. 这通常一个抽象基类。

Then, when you decide what processing you really want to do on each node, you derive a new class from visitor that overrides visit to do the per-node processing. 然后,当您决定在每个节点上确实要执行哪些处理时,您将从visitor派生一个新类来覆盖visit以执行每个节点的处理。 The tree needs no modification at all. 树根本不需要修改。 It's well-known pattern (even among C++ programmers, who are generally a lot less "friendly" toward patterns that others) so assuming you use the normal names, most will recognize it quite quickly and easily. 这是众所周知的模式(即使在C ++程序员中,他们对其他人的模式通常不那么“友好”)所以假设你使用普通名称,大多数人会很快和容易地认出它。 It also helps that the code itself is normally pretty trivial -- the accept normally looks something like this: 这也有助于代码本身通常非常简单 - accept通常看起来像这样:

struct tree_node;

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

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

    // ...
};

Then to add processing, you derive from visitor, and override visit to do the processing you need. 然后要添加处理,您从访问者派生,并覆盖visit以执行您需要的处理。 The tree side of things needs no modification at all. 树的一面根本不需要修改。 This also has the nicety that a single visitor class can be written to visit more than one type of node (even if the nodes are not related by inheritance). 这也很精确,可以编写单个访问者类来访问多种类型的节点(即使节点不通过继承关联)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM