The C++ logo, by Jeremy Kratz, licensed under CC0 1.0 Universal Public Domain Dedication

dynamic_cast and multiple inheritance

Notes published the
3 - 4 minutes to read, 828 words

The setup

You have an interface, which defines some pure virtual method, and a set of classes that derive from it:

struct interface {
    virtual void foo() = 0;
    virtual void bar() = 0;
    virtual ~interface() = default;
};


struct derived1 : interface{
    void foo() override;
    void bar() override;
    void baz();
};
struct derived2 : interface{
    void foo() override;
    void bar() override;
    void baz();
};
struct derived3 : interface{
    void foo() override;
    void bar() override;
};

// other classes

Then there is a function that takes an interface*, and the implementation calls some function on it:

void fun(interface* i){
    i->bar();
    i->foo();
};

A problem arises

Now some classes (let’s say derived1 and derived2), even if they implement the interface, are more special than others, and you would like to treat them differently inside of fun.

There are two possibilities:

  • Extend interface* by adding member functions that are relevant only for derived1 and derived2

  • add a dynamic_cast

Extending interface* might not be possible, as the class might be defined in another library. Also changing the signature of fun should be avoided, as it should still handle all possible interface* (and it might be a callback, so we simply cannot change it)

dynamic_cast

dynamic_cast is normally presented as the tool to cast down the class hierarchy, and would solve the issue in this case:

void fun(interface* i){
    {
        auto ptr = dynamic_cast<derived1*>(i);
        if(ptr){
            // do something special with ptr
            return;
        }
    }
    {
        auto ptr = dynamic_cast<derived2*>(i);
        if(ptr){
            // do something special with ptr
            return;
        }
    }
    i->bar();
    i->foo();
};

Multiple inheritance to the rescue

The more special cases you have, the more verbose the code gets. It would be nice if there would be a way to reduce such code duplication.

derived1 and derived2 are different from the other classes, but it might be possible to encapsulate such functionality in a member function baz.

If this is possible, we can remove some duplication by adding another interface!

struct interface {
    virtual void foo() = 0;
    virtual void bar() = 0;
    virtual ~interface() = default;
};

struct interface2 {
    virtual void baz() = 0;
    virtual ~interface2() = default;
};


struct derived1 : interface, interface2 {
    void foo() override;
    void bar() override;
    void baz() override;
};
struct derived2 : interface, interface2 {
    void foo() override;
    void bar() override;
    void baz() override;
};
struct derived3 : interface {
    void foo() override;
    void bar() override;
};

// other classes

void fun(interface* i){
    auto ptr = dynamic_cast<interface2*>(i);
    if(ptr){
        ptr->baz();
        return;
    }
    i->bar();
    i->foo();
};

Thanks to interface2, it is possible to remove the duplicated logic that was inside fun.

This might look like a magic trick, interface and interface2 are two separate classes, and both have no idea of the other class.

Yet it is possible to dynamic_cast one class to the other (in both directions).

This is because dynamic_cast is not only useful for "downcasting", ie casting from a base class to a derived class.

It can be also used for casting a derived class to a base class (unnecessary as this conversion is implicit).

And it can be used, like in this example, for casting up and down the class hierarchy at the same time.

Was multiple inheritance necessary?

No, it would also have sufficed to add an intermediate class in a single-file hierarchy

struct interface {
    virtual void foo() = 0;
    virtual void bar() = 0;
    virtual ~interface() = default;
};

struct interface2 : interface {
    virtual void baz() = 0;
};


struct derived1 : interface2 {
    void foo() override;
    void bar() override;
    void baz() override;
};
struct derived2 : interface2 {
    void foo() override;
    void bar() override;
    void baz() override;
};
struct derived3 : interface {
    void foo() override;
    void bar() override;
};

// other classes

void fun(interface* i){
    auto ptr = dynamic_cast<interface2*>(i);
    if(ptr){
        ptr->baz();
        return;
    }
    i->bar();
    i->foo();
};

The code inside fun is unchanged, and derived1 and derived2 need to specify only a base class.

In such minimal examples, there is no difference, but in a bigger project, having interface2 not depending on interface can help to avoid unnecessary dependencies between classes, projects, and/or libraries.

Deep public virtual hierarchies confound me, so I might prefer the solution with two base classes to keep it flatter, but I suppose that having N base classes has the same complexity as having a class hierarchy N level deep, so I do not think that the stylistic difference is that relevant.

Another possible advantage of having multiple base classes is that they can have different visibilities.

Thus one can have a public interface base class and a private interface2 base class.

Even if private inheritance is useful, in this case, it would not be useful as the dynamic_cast would fail (return nullptr).

Obviously, multiple inheritance has disadvantages too, like

But for this use case, extrapolating a new interface from a class hierarchy, those are non issues.


Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.