C++ Inherit from Multiple Base Classes with the Same Virtual Function Name

Two base classes with the same pure virtual function

Yes, this code is well-formed and void func() overrides both A::func() and B::func(). From the C++14 standard:

[class.virtual]


  1. If a virtual member function vf is declared in a
    class Base and in a class Derived, derived directly or indirectly
    from Base, a member function vf with the same name,
    parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or
    absence of same) as Base::vf is declared, then Derived::vf is also
    virtual (whether or not it is so declared) and it overrides
    Base::vf.

C++ multiple inheritance from different abstract bases with same pure virtual method

The most vexing parse strikes again (have you turned on your compiler warnings?):

C c();

is a function declaration therefore no object was constructed. Anyway there would be no ambiguity if you had declared c as

C c;

since the two pure virtual functions would be overridden by the most derived C::f().

Ambiguity would arise if you were to write something like

class A
{
public: void f() {}
};

class B
{
public: void f() {}
};

class C : public A,B
{
public:

};

int main() {

C c;
c.f(); // Need to call a base one, but which one?

}

error: member 'f' found in multiple base classes of different types

Can multiple base classes have the same virtual method?

Can multiple base classes have the same virtual method?


  1. Can this happen?

Yes.

So C read() method would actually call class B read()

That doesn't happen automatically. A member function of base doesn't override a function of an unrelated base.

You can add an override to C:

class C : public A, public B {
int get_data() override;
}

This overrides both A::get_data and B::get_data. In order to "actually call class B read()", you can indeed make such call:

int C::get_data() {
return B::get_data();
}

Or... that would be possible if you hadn't declared B::get_data private.


Overriding a function in another base without explicitly delegating in derived is possible if you change your hierarchy a bit. In particular, you need a common base, and virtual inheritance:

struct Base {
virtual int get_data() = 0;
};

struct A : virtual Base {
void print() {
std::cout << get_data();
}
};

struct B : virtual Base {
int get_data() override {
return 4;
}
};

struct C : A, B {};

How to implement virtual functions with the same name in multiple inheritance

class C_a 
: public A
{
virtual void F_A() = 0;
virtual void F() { this->F_A() };
};

class C_b
: public B
{
virtual void F_B() = 0;
virtual void F() { this->F_B() };
};

class C
: public C_a
, public C_b
{
void F_A() { ... }
void F_B() { ... }
};

If I'm remembing right the ISO committee thought about this problem and discussed a change of the language. But then ... somebody found this nice way to solve this problem :-)

Your second solution is better in case your are able to change your class hierarchy. You may have a lock at http://www.gotw.ca/publications/mill18.htm for a description why it is better.

C++ multiple inheritance - same method names - can I somehow remove one of them?

Since you said that you don't need A's version of those methods, you could write

struct AB : public A, public B
{
void foo() override {}
void foo2() override {}

using B::setId;
using B::getId;
};

This will put B's implementation of those methods into AB's namespace and make calling them unambiguous.

C++ multiple inheritance from base classes with members with same name

I'd put the common parts of mode1 and mode2 in a common base class, let's say Common, comprising then your data and member function operation2. Then, together with virtual inheritance, you can have two views on the same data, even at the same time if needed.

class common {
friend class mode1;
friend class mode2;
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;

public:
virtual void operation2() final { // do something
};

};

class mode1 : public virtual common
{
public:
virtual void operation1() final { // do something
};
virtual void operation3() final { // do something }
};
};

class mode2 : public virtual common
{
public:
virtual void operation4() final { // do something
}
virtual void operation5() final { // do something
}
};

class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
};
void operation7() { // do something }
};
};

Multiple inheritance in c++ with virtual functions

This looks like the classic "diamond inheritance problem" where you have a virtual method defined in a base class which is inherited by two or more other classes which are used as source for multiple inheritance.

Anyway - virtual inheritance is the answer to your problem

class A
{
public:
virtual void FA()=0;
}

class B: virtual public A
{
public:
virtual void FB()=0; //probably a pure virtual function as well!?
}

class Imp_A: virtual public A
{
public:
void FA()
{
// implement FA
}
}

class Imp_B :public Imp_A, public B //since you call FA() you need it's implementation from Imp_A
{
public:
void FB()
{
// implement FB by calling FA()
FA();
// do some more work here.
}
}

The problem here is that Imp_b ends up with to definition for FA coming from two class A definitions; using virtual inheritance helps solve this issue.

Understanding Virtual Functions when Deriving from Multiple Classes

What you must first understand here is that the actual object created, *o is of type C12 - because that's what you constructed with new C12().

Next, with virtual functions, then the member for the actual object will be called, no matter what 'type' you cast a pointer to. So, when you cast to an I2 pointer in I2 *o = new C12(), it doesn't matter to the underlying object if, for example, you then call o->g(), as the object would 'know' to call its overridden function.

However, when you the cast the pointer to the 'unrelated' I1* you get into strange ground. Bearing in mind that the classes I1 and I2 have, essentially, identical memory layouts, then calling f() in one would be directing to the same 'offset' as calling g() in the other. But, as o is actually a pointer to I2, the v-table entry the call ends up with is the offset of g in I2 - which is overridden by C12.

It's also noteworthy that you've used a C-style cast to get from I2* to I1* (but you could also use a reinterpret_cast). This is important, because both of these do absolutely nothing to the pointer, or to the object/memory pointed to.

Probably sounds a bit garbled, but I hope it offers some insight!

Here's a possible memory layout/scenario - but it's going to be implementation-specific, and using the class pointer after the C-style cast may well constitute undefined behaviour!

Possible memory map (simplified, assuming 4-bytes for all components):

class I1:
0x0000: (non-virtual data for class I1)
0x0004: v-table entry for function "f"

class I2:
0x0000: (non-virtual data for class I2)
0x0004: v-table entry for function "g"

class C12:
0x0000: (non-virtual data for class I1)
0x0004: v-table entry for function "f"
0x0008: (non-virtual data for class I2)
0x000C: v-table entry for function "g"
0x0010: (class-specific stuff for C12)

Now, when you do the conversion from a C12* to I2* in I2 *o = new C12();, the compiler understands the relation between the two classes, so o will point to the 0x0008 offset in C12 (the derived class has been correctly 'sliced'). But the C-style cast from I2* to I1* doesn't change anything, so the compiler 'thinks' it points to an I1 but it's still pointing to an actual I2 slice of C12 - and this 'looks' just like a real I1 class.

Homework Assignment

What you may find interesting (and may or may not concur with the memory layout I've described) is to add the following code towards the end of main():

C12* properC12 = new C12();// Points to the 'origin' of the class
I1* properI1 = properC12; // Should (?) have same value as above?
I2* properI2 = properC12; // Should (?) have an offset to 'slice'
I1* dodgyI1 = (I1*)properC12; // Will (?) have same value as properI2!
cout << std::hex << properC12 << endl;
cout << std::hex << properI1 << endl;
cout << std::hex << properI2 << endl;
cout << std::hex << dodgyI1 << endl;

Please - anyone who tries - let us know what the values are, and what platform/compiler you are using. In Visual Studio 2019, compiling for the x64 platform, I get these pointer values:

000002688A9726E0
000002688A9726E0
000002688A9726E8
000002688A9726E0

... which (sort of) concurs with the memory layout I described (other than having the v-tables somewhere else, rather than 'in-block').



Related Topics



Leave a reply



Submit