Search code examples
c++methodsvirtualsubclassbase-class

Calling identically named methods in base classes


Base class A has a subclass B, and B has a subclass C. A implements a virtual method doStuff(), B does not, and C does. From C, I want to call A's implementation of doStuff() (I do this from within C's implementation of doStuff() but that shouldn't really matter.) Should I call:

A::doStuff();

Or:

B::doStuff();

The first seems more clear because it refers to the actual implementation. On the other hand, the second might be more useful if I later decide that B needs to doStuff() differently than A. Which is more standard? Which is more dangerous?

I was a little surprised that B::doStuff() didn't trigger any warnings, but I suppose that by definition B has an implementation even if it's from the base class. Can the chain of base classes and implementations be arbitrarily long and complicated? For example, if I had A through Z, each a subclass of the previous one, could I have implementations anywhere in the chain, and does calling any class's method just go 'up the chain' until it finds an implementation?


Solution

  • First, in regards to the last paragraph, yes, you're absolutely right.

    Next up, for the main question:

    It's a matter of logic which should be treated on a case-to-case basis. You're the only one who can decide whether you want to call the logic from B or from A, even if B::doStuff() isn't yet implemented. There's no right answer.

    However, you should look into the template method pattern. My guess is that if you want C to call something from A and later you decide that you want D (sibling of C) to do the same thing, you're duplicating code. So I'd take a different approach:

    class A
    {
    private:
       void doAStuff(); //doesn't have to be here
    public:
       virtual void doStuff()
       {
          doAStuff();
       }
       void doAStuffAndStuff();
       {
          doAStuff();
          doStuff();
       }
    }
    
    class B : A
    {
    public:
       virtual void doStuff(); //or not
    }
    
    class C : B
    {
    public:
       virtual void doStuff();
    }
    

    IMO this is more intuitive. Say I have an A* a, I don't know the dynamic type of a:

    • if I want to call doStuff() with the logic in the most derived class, I call doStuff().
    • if I want to call doStuff() with the logic in the most derived class, but I also want the logic from A, I call doAStuffAndStuff().

    I'm stating this approach as an alternative. I'm not saying it necessarily applies to your case, but it might.