Zach Rattner
Zach Rattner

Reputation: 21353

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. 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.

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)? Or, should I make the tree class abstract and implement one sub-class for the application-specific part.

Upvotes: 5

Views: 742

Answers (3)

Jerry Coffin
Jerry Coffin

Reputation: 490128

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.

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 .

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. The visitor class is a typically-abstract base class with a member function (traditionally named visit) for doing processing on a node. 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. 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. It also helps that the code itself is normally pretty trivial -- the accept normally looks something like this:

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. 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).

Upvotes: 1

Lou Franco
Lou Franco

Reputation: 89172

I wouldn't decide that now. Later you could make an abstract base class for Tree and move code there, if that made sense.

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.

Upvotes: 2

Tony Delroy
Tony Delroy

Reputation: 106096

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. If the tree can equally be used with the other parts of the interface (e.g. 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. In that case, onNodeVisited should not be pure virtual, now or ever. 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.

Upvotes: 1

Related Questions