C++ Best Practices – Handling Unused Inherited Virtual Functions

cpatterns-and-practicesprogramming practices

Let's assume I have a hierarchy of several classes each derived from each other using virtual functions. The base class acts as an interface and defines functions, which may not be required by all of the derived classes. Now I wonder what the best practice is for such intermediate functions. Should those still be implemented and simple chain down to the base class, or should they be skipped?
The problem I have is, that implementing such a function, when it isn't needed, simply adds noise to the code. On the other hand, when using a higher class, in C++ you can not simply call a superclass method (like in Java), so you need to know in which base class the method is really implemented. And to my mind, this would violate encapsulation, because I would need to know some implementation details of the base class.

To illustrate what I mean here is an artifical exmaple:

class MyInterface
{
    virtual void foo(bool) = 0;
    virtual void foo1(int) = 0;
};

class Base : public MyInterface
{
    void foo(bool param) override
    {
        // do some stuff here.
    }

    void foo1(int param) override
    {
        // do some stuff here.
    }
};


class A : Base
{
    void foo(bool param) override
    {
        // do some stuff here.
        Base::foo(param);
    }
    // class doesn't need foo1() here so it is not implemented.
};


class B : A
{
    void foo(bool param) override
    {
        // do some stuff here.
        A::foo(param);
    }

    void foo1(int param) override
    {
        // This class needs foo1() but as A doesn't have it implemented
        // I have to knwo this and skip A going directly to Base
        Base::foo1(1);
    }
};

Here the class B needs to know that A doesn't implement foo1 and has to skip it. Of course this can be seen in the header, but if I want to later change class A it shouldn't be neccessary to touch all the other classes that are derived from it (encapsulation and isolation principle) So even worse, if I implement now foo1() in A later, B would still skip it and never realise that the functionality has changed.

So from an object model point of view I would assume that such empty functions should still be designed into a class?

Best Answer

You're right, this is a flaw in the language itself which means that despite all the advantages which come from inheritance, you still have to know how it is implemented. It would be ideal to be able to call super within the method and have it call the appropriate method, whatever that might be. This is one of the things that Java tried to improve upon.

As you said, you could get around it by implementing every method in class A, and so in doing so, you prevent complications later on. It is a bit of a pain, but I suppose it is a necessary evil for creating a proper library to be used by others. Note that only the class being made available in the library for use has to implement all virtual methods, and not any internally used classes unless you require otherwise.

While not a solution, a slightly friendlier approach (albeit a little hacky) might be to typedef "super" as a private type definition for the base class. As in the case of class B, you'd have:

private:  
    typedef A super;

Likewise in class A, you could define:

private:
    typedef Base super;

At least if you're the one writing the code, rather than have to know the name of the class, you could do:

class B : A
{
    void foo(bool param) override
    {
        // do some stuff here.
        super::foo(param);
    }

    void foo1(int param) override
    {
        super::super::foo1(1);
    }
};

Again, while this doesn't fix your problem, it makes it slightly more manageable.

See here for more details.