39

I'm using multiple inheritance in C++ and extending base methods by calling their base explicitly. Assume the following hierarchy:

     Creature
    /        \
 Swimmer    Flier
    \        /
       Duck

Which corresponds to

class Creature
{
    public:
        virtual void print()
        {
            std::cout << "I'm a creature" << std::endl;
        }
};

class Swimmer : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            std::cout << "I can swim" << std::endl;
        }
};

class Flier : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            std::cout << "I can fly" << std::endl;
        }
};

class Duck : public Flier, public Swimmer
{
     public:
        void print()
        {
            Flier::print();
            Swimmer::print();
            std::cout << "I'm a duck" << std::endl;
        }
};

Now this presents a problem - calling the duck's print method calls its respective base methods, all of which in turn call the Creature::print() method, so it ends up being called twice-

I'm a creature
I can fly
I'm a creature
I can swim
I'm a duck

I would like to find a way to make sure the base method is called only once. Something similar to the way virtual inheritance works (calling the base constructor on the first call, then only assigning a pointer to it on successive calls from other derived classes).

Is there some built-in way to do this or do we need to resort to implementing one ourselves?

If so, how would you approach this?

The question isn't specific to printing. I wondered if there's a mechanism for extending base methods and functionality while keeping the call order and avoiding the diamond problem.

I understand now that the most prominent solution would be to add helper methods, but I just wondered if there's a "cleaner" way.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
O. Aroesti
  • 1,016
  • 11
  • 32
  • 17
    Both Flier and Swimmer's print explicitly calls Creature's print. If I were you I would make an attempt to solve the problem without this inheritance. "Composition over inheritance." For example ECS (entity component system) is exactly about to put properties together in a flexible way without the inheritance hell. – titapo Apr 24 '19 at 12:23
  • 2
    add (protected)method to your extra `std::cout`, so you can choose which version to call exactly. – Jarod42 Apr 24 '19 at 12:24
  • 1
    @titapo that is a good suggestion, please post it as an answer instead of a comment. – Captain Man Apr 24 '19 at 19:24
  • 1
    As other people posted solutions already, I'd like to emphasize here, that you do not really want to use multiple inheritance and especially a diamond inheritance at all: https://stackoverflow.com/questions/406081/why-should-i-avoid-multiple-inheritance-in-c – Lukas Plazovnik Apr 24 '19 at 20:24
  • I use multiple inheritances ALL the time professionally. It's not a bad thing, just that most people don't understand the repercussions of multiple inheritances and how to avoid digging yourself into a hole. It's wonderful for MVC and all manner of GUI stuff! – Wilfred Smith Apr 25 '19 at 11:04
  • It's a bad thing precisely because there are so many repercussions and it is so easy to dig yourself into a hole. Code is cleaner and clearer without it. Profesionally. – Lightness Races in Orbit Apr 25 '19 at 11:34

7 Answers7

50

Most likely this is a XY problem. But ... just don't call it twice.

#include <iostream>

class Creature
{
public:
    virtual void identify()
    {
        std::cout << "I'm a creature" << std::endl;
    }
};

class Swimmer : public virtual Creature
{
public:
    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a swimmer\n";
    }

    virtual void tell_ability()
    {
        std::cout << "I can swim\n";
    }
};

class Flier : public virtual Creature
{
public:
    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a flier\n";
    }

    virtual void tell_ability()
    {
        std::cout << "I can fly\n";
    }
};

class Duck : public Flier, public Swimmer
{
public:
    virtual void tell_ability() override
    {
        Flier::tell_ability();
        Swimmer::tell_ability();
    }

    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a duck\n";
    }
};

int main()
{
    Creature c;
    c.identify();
    std::cout << "------------------\n";

    Swimmer s;
    s.identify();
    std::cout << "------------------\n";

    Flier f;
    f.identify();
    std::cout << "------------------\n";

    Duck d;
    d.identify();
    std::cout << "------------------\n";
}

Output:

I'm a creature
------------------
I'm a creature
I can swim
I'm a swimmer
------------------
I'm a creature
I can fly
I'm a flier
------------------
I'm a creature
I can fly
I can swim
I'm a duck
------------------
Swordfish
  • 12,971
  • 3
  • 21
  • 43
23

We can let the base class keep track of the attributes:

#include <iostream>
#include <string>
#include <vector>

using namespace std::string_literals;

class Creature
{
public:
    std::string const attribute{"I'm a creature"s};
    std::vector<std::string> attributes{attribute};
    virtual void print()
    {
        for (auto& i : attributes)
            std::cout << i << std::endl;
    }
};

class Swimmer : public virtual Creature
{
public:
    Swimmer() { attributes.push_back(attribute); }
    std::string const attribute{"I can swim"s};
};

class Flier : public virtual Creature
{
public:
    Flier() { attributes.push_back(attribute); }
    std::string const attribute{"I can fly"s};
};

class Duck : public Flier, public Swimmer
{
public:
    Duck() { attributes.push_back(attribute); }
    std::string const attribute{"I'm a duck"s};
};

int main()
{
    Duck d;
    d.print();
}

Likewise, if it is not just printing we're after, but rather the function calls, then we could let the base class keep track of the functions:

#include <iostream>
#include <functional>
#include <vector>

class Creature
{
public:
    std::vector<std::function<void()>> print_functions{[this] {Creature::print_this(); }};
    virtual void print_this()
    {
        std::cout << "I'm a creature" << std::endl;
    }
    void print()
    {
        for (auto& f : print_functions)
            f();
    }
};

class Swimmer : public virtual Creature
{
public:
    Swimmer() { print_functions.push_back([this] {Swimmer::print_this(); }); }
    void print_this()
    {
        std::cout << "I can swim" << std::endl;
    }
};

class Flier : public virtual Creature
{
public:
    Flier() { print_functions.push_back([this] {Flier::print_this(); }); }
    void print_this()
    {
        std::cout << "I can fly" << std::endl;
    }
};

class Duck : public Flier, public Swimmer
{
public:
    Duck() { print_functions.push_back([this] {Duck::print_this(); }); }
    void print_this()
    {
        std::cout << "I'm a duck" << std::endl;
    }
};

int main()
{
    Duck d;
    d.print();
}
wally
  • 10,717
  • 5
  • 39
  • 72
  • 1
    That makes every instance allocate an entire `std::vector`, which is a bit heavy. – Kevin Apr 24 '19 at 19:35
  • 6
    @Kevin I'm guessing you won't like vtables either. :) – wally Apr 24 '19 at 22:57
  • @wally vtables involve storing a pointer to a bit of read-only memory - can often be done in a single move instruction. Vectors involve allocating memory from the heap, which is going to involve grabbing and releasing a mutex, with all the memory barrier and cache-flushing that implies. – Martin Bonner supports Monica Apr 25 '19 at 12:18
  • 1
    @MartinBonner Other threads wouldn't have access to the object until after construction. Why would you need to lock a mutex for the vector? And where did you hear that you need a mutex for heap allocation? – wally Apr 25 '19 at 13:23
  • @wally - it's the heap allocation that needs the mutex. The heap is a shared data structure in all the implementations I am familiar with, so it needs a mutex. (You can allocate from a per-thread heap, but you need to support freeing from an arbitrary thread - so you still need a mutex.) – Martin Bonner supports Monica Apr 25 '19 at 13:44
  • 1
    @MartinBonner If you're referring to the locking that the operating system does then it is unavoidable. I suppose that if you create the vector with the correct size then the extra malloc might be optimized away if you're creating the entire object on the heap anyway. The contention between threads might also be reduced if the OS decided to use separate arenas for the allocations. The OS might also not be using a mutex. In general, defensively designing around this would be impractical. std::vector is not that bad. – wally Apr 25 '19 at 14:36
9

An easy way is to create a bunch of helper classes that mimick the inheritance structure of your main hierarchy and do all the printing in their constructors.

 struct CreaturePrinter {
    CreaturePrinter() { 
       std::cout << "I'm a creature\n";
    }
 };

 struct FlierPrinter: virtual CreaturePrinter ... 
 struct SwimmerPrinter: virtual CreaturePrinter ...
 struct DuckPrinter: FlierPrinter, SwimmerPrinter ...

Then each print method in the main hierarchy just creates the corresponding helper class. No manual chaining.

For maintainability you can make each printer class nested in its corresponding main class.

Naturally in most real world cases you want to pass a reference to the main object as an argument to the constructor of its helper.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
6

Your explicit calls to the print methods form the crux of the issue.

One way round this would be to drop the print calls, and replace them with say

void queue(std::set<std::string>& data)

and you accumulate the print messages into the set. Then it doesn't matter those functions in the hierarchy get called more than once.

You then implement the printing of the set in a single method in Creature.

If you want to preserve the order of printing, then you'd need to replace the set with another container that respects the order of insertion and rejects duplicates.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
6

If you want that middle class method, do not call the base class method. The easiest and simplest way is to extract extra methods, and then reimplementing Print is easy.

class Creature
{
    public:
        virtual void print()
        {
            std::cout << "I'm a creature" << std::endl;
        }
};

class Swimmer : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            detailPrint();
        }

        void detailPrint()
        {
            std::cout << "I can swim" << std::endl;
        }
};

class Flier : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            detailPrint();
        }

        void detailPrint()
        {
            std::cout << "I can fly" << std::endl;
        }
};

class Duck : public Flier, public Swimmer
{
     public:
        void print()
        {
            Creature::Print();
            Flier::detailPrint();
            Swimmer::detailPrint();
            detailPrint();
        }

        void detailPrint()
        {
            std::cout << "I'm a duck" << std::endl;
        }
};

Without details what is your actual problem is, it hard to come up with a better solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marek R
  • 32,568
  • 6
  • 55
  • 140
4

Use:

template<typename Base, typename Derived>
bool is_dominant_descendant(Derived * x) {
    return std::abs(
        std::distance(
            static_cast<char*>(static_cast<void*>(x)),
            static_cast<char*>(static_cast<void*>(dynamic_cast<Base*>(x)))
        )
    ) <= sizeof(Derived);
};

class Creature
{
public:
    virtual void print()
    {
        std::cout << "I'm a creature" << std::endl;
    }
};

class Walker : public virtual Creature
{
public:
    void print()
    {
        if (is_dominant_descendant<Creature>(this))
            Creature::print();
        std::cout << "I can walk" << std::endl;
    }
};

class Swimmer : public virtual Creature
{
public:
    void print()
    {
        if (is_dominant_descendant<Creature>(this))
            Creature::print();
        std::cout << "I can swim" << std::endl;
    }
};

class Flier : public virtual Creature
{
public:
    void print()
    {
        if (is_dominant_descendant<Creature>(this))
            Creature::print();
        std::cout << "I can fly" << std::endl;
    }
};

class Duck : public Flier, public Swimmer, public Walker
{
public:
    void print()
    {
        Walker::print();
        Swimmer::print();
        Flier::print();
        std::cout << "I'm a duck" << std::endl;
    }
};

And with Visual Studio 2015 the output is:

I'm a creature
I can walk
I can swim
I can fly
I'm a duck

But is_dominant_descendant does not have a portable definition. I wish it were a standard concept.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Red.Wave
  • 2,790
  • 11
  • 17
3

You are asking for something like inheritance on a function level that automatically calls the inherited function and just adds more code. Also you want it to be done in a virtual way just like class inheritance. Pseudo syntax:

class Swimmer : public virtual Creature
{
     public:
        // Virtually inherit from Creature::print and extend it by another line of code
        void print() : virtual Creature::print()
        {
            std::cout << "I can swim" << std::endl;
        }
};

class Flier : public virtual Creature
{
     public:
        // Virtually inherit from Creature::print and extend it by another line of code
        void print() : virtual Creature::print()
        {
            std::cout << "I can fly" << std::endl;
        }
};

class Duck : public Flier, public Swimmer
{
     public:
        // Inherit from both prints. As they were created using "virtual function inheritance",
        // this will "mix" them just like in virtual class inheritance
        void print() : Flier::print(), Swimmer::print()
        {
            std::cout << "I'm a duck" << std::endl;
        }
};

So the answer to your question

Is there some built-in way to do this?

is no. Something like this does not exist in C++. Also, I'm not aware of any other language that has something like this. But it is an interesting idea...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sebrockm
  • 5,733
  • 2
  • 16
  • 39
  • My compiler (VS C++17) doesn't allow this syntax `void print() : virtual Creature::print()` for the method declarations and it's expecting a semicolon to end the statement where you put a colon. Is this something only certain compilers allow? – MasterHD Oct 25 '21 at 09:17
  • @MasterHD I'm sorry for the confusion, you probably missed the most important parts of my answer :) Right befor the code block I say "**Pseudo** syntax:" And after the code block "Something like this does not exist in C++" – sebrockm Oct 25 '21 at 11:11