Multiple Inheritance + Virtual Function Mess

Virtual function in a multiple inheritance class

This is called function overloading, so you can not do what you want automatically.

You have to do it like this:

#include <iostream>

class A {
public:
virtual void foo() {
std::cout << "foo of A\n";
}
};

class B {
public:
// virtual keyword is not needed,
// but it's to write it so that you
// remind the reader that foo is virtual
virtual void foo() {
std::cout << "foo of B\n";
}
};

class C {
public:
virtual void foo() {
std::cout << "foo of C\n";
}
};

class D : public A, public B, public C {
public:
virtual void foo() {
A::foo();
B::foo();
C::foo();
std::cout << "foo of D\n";
}
};

int main()
{
D d_object;
d_object.foo();
return 0;
}

However, I strongly suggest you to read this answer, since you probably receive:

has virtual method..but not virtual constructor

Usually warnings are wise to be treated like real errors, since they usually expose logical errors.

Inheritance mess with pure virtual functions, any way to not have to re-define them all?

The issue is that you actually are getting two pureVirtual functions. C++ isn't smart enough to merge the two by default. You can use virtual inheritance to force the behavior.

#include <iostream>

struct Parent {
virtual void pureVirtual() = 0;
};

struct Child : virtual public Parent {
void pureVirtual() override {
std::cout << "Child::pureVirtual()" << std::endl;
}
};

struct Brother : virtual public Parent {};

struct GrandChild : public Child, public Brother {};

int main() {
GrandChild test;
}

What is the exact problem with multiple inheritance?

The most obvious problem is with function overriding.

Let's say have two classes A and B, both of which define a method doSomething. Now you define a third class C, which inherits from both A and B, but you don't override the doSomething method.

When the compiler seed this code...

C c = new C();
c.doSomething();

...which implementation of the method should it use? Without any further clarification, it's impossible for the compiler to resolve the ambiguity.

Besides overriding, the other big problem with multiple inheritance is the layout of the physical objects in memory.

Languages like C++ and Java and C# create a fixed address-based layout for each type of object. Something like this:

class A:
at offset 0 ... "abc" ... 4 byte int field
at offset 4 ... "xyz" ... 8 byte double field
at offset 12 ... "speak" ... 4 byte function pointer

class B:
at offset 0 ... "foo" ... 2 byte short field
at offset 2 ... 2 bytes of alignment padding
at offset 4 ... "bar" ... 4 byte array pointer
at offset 8 ... "baz" ... 4 byte function pointer

When the compiler generates machine code (or bytecode), it uses those numeric offsets to access each method or field.

Multiple inheritance makes it very tricky.

If class C inherits from both A and B, the compiler has to decide whether to layout the data in AB order or in BA order.

But now imagine that you're calling methods on a B object. Is it really just a B? Or is it actually a C object being called polymorphically, through its B interface? Depending on the actual identity of the object, the physical layout will be different, and its impossible to know the offset of the function to invoke at the call-site.

The way to handle this kind of system is to ditch the fixed-layout approach, allowing each object to be queried for its layout before attempting to invoke the functions or access its fields.

So...long story short...it's a pain in the neck for compiler authors to support multiple inheritance. So when someone like Guido van Rossum designs python, or when Anders Hejlsberg designs c#, they know that supporting multiple inheritance is going to make the compiler implementations significantly more complex, and presumably they don't think the benefit is worth the cost.

reduce size of object (wasted) in Multi virtual inheritance

EDIT: based on latest update to the question and some chatting

Here's the most compact maintaining the virtual in all your classes.

#include <iostream>
#include <vector>

using namespace std;

struct BaseFields {
int entityId{};
int16_t componentId{};
int8_t typeId{};
int16_t hpIdx;
int16_t flyPowerIdx;
};

vector<int> hp; // this will contain all the hit points, dynamically resizable, logic up to you
vector<float> flyPower; // this will contain all the fly powers, dynamically resizable, logic up to you

class BaseComponent {
public: // or protected
BaseFields data;
};
class HpOO : public virtual BaseComponent {
public:
void damage() {
hp[data.hpIdx] -= 1;
}
};
class FlyableOO : public virtual BaseComponent {
public:
void addFlyPower(float power) {
flyPower[data.hpIdx] += power;
}
};
class BirdOO : public virtual HpOO, public virtual FlyableOO {
public:
void suicidalFly() {
damage();
addFlyPower(5);
}
};

int main (){
std::cout<<"Base="<<sizeof(BaseComponent)<<std::endl; // 12
std::cout<<"C="<<sizeof(HpOO)<<std::endl; // 24
std::cout<<"D="<<sizeof(FlyableOO)<<std::endl; // 24
std::cout<<"E="<<sizeof(BirdOO)<<std::endl; // 32
}

much smaller class size version dropping all the virtual class stuff:

#include <iostream>
#include <vector>

using namespace std;

struct BaseFields {
};

vector<int> hp; // this will contain all the hit points, dynamically resizable, logic up to you
vector<float> flyPower; // this will contain all the fly powers, dynamically resizable, logic up to you

class BaseComponent {
public: // or protected
int entityId{};
int16_t componentId{};
int8_t typeId{};
int16_t hpIdx;
int16_t flyPowerIdx;
protected:
void damage() {
hp[hpIdx] -= 1;
};
void addFlyPower(float power) {
flyPower[hpIdx] += power;
}
void suicidalFly() {
damage();
addFlyPower(5);
};
};
class HpOO : public BaseComponent {
public:
using BaseComponent::damage;
};
class FlyableOO : public BaseComponent {
public:
using BaseComponent::addFlyPower;
};
class BirdOO : public BaseComponent {
public:
using BaseComponent::damage;
using BaseComponent::addFlyPower;
using BaseComponent::suicidalFly;
};

int main (){
std::cout<<"Base="<<sizeof(BaseComponent)<<std::endl; // 12
std::cout<<"C="<<sizeof(HpOO)<<std::endl; // 12
std::cout<<"D="<<sizeof(FlyableOO)<<std::endl; // 12
std::cout<<"E="<<sizeof(BirdOO)<<std::endl; // 12
// accessing example
constexpr int8_t BirdTypeId = 5;
BaseComponent x;
if( x.typeId == BirdTypeId ) {
auto y = reinterpret_cast<BirdOO *>(&x);
y->suicidalFly();
}
}

this example assumes your derived classes do not have overlapping functionalities with diverging effects, if you have those you have to add virtual functions to your base class for an extra overhead of 12 bytes (or 8 if you pack the class).

and quite possibly the smallest version still maintaining the virtuals

#include <iostream>
#include <vector>

using namespace std;

struct BaseFields {
int entityId{};
int16_t componentId{};
int8_t typeId{};
int16_t hpIdx;
int16_t flyPowerIdx;
};

#define PACKED [[gnu::packed]]

vector<int> hp; // this will contain all the hit points, dynamically resizable, logic up to you
vector<float> flyPower; // this will contain all the fly powers, dynamically resizable, logic up to you

vector<BaseFields> baseFields;

class PACKED BaseComponent {
public: // or protected
int16_t baseFieldIdx{};
};
class PACKED HpOO : public virtual BaseComponent {
public:
void damage() {
hp[baseFields[baseFieldIdx].hpIdx] -= 1;
}
};
class PACKED FlyableOO : public virtual BaseComponent {
public:
void addFlyPower(float power) {
flyPower[baseFields[baseFieldIdx].hpIdx] += power;
}
};
class PACKED BirdOO : public virtual HpOO, public virtual FlyableOO {
public:
void suicidalFly() {
damage();
addFlyPower(5);
}
};

int main (){
std::cout<<"Base="<<sizeof(BaseComponent)<<std::endl; // 2
std::cout<<"C="<<sizeof(HpOO)<<std::endl; // 16 or 10
std::cout<<"D="<<sizeof(FlyableOO)<<std::endl; // 16 or 10
std::cout<<"E="<<sizeof(BirdOO)<<std::endl; // 24 or 18
}

the first number is for unpacked structure, second packed

You can also pack the hpIdx and flyPowerIdx into the entityId using the union trick:

union {
int32_t entityId{};
struct {
int16_t hpIdx;
int16_t flyPowerIdx;
};
};

in the above example if not using packing and moving the whole BaseFields structure into the BaseComponent class the sizes remain the same.

END EDIT

Virtual inheritance just adds one pointer size to the class, plus alignment of the pointer (if needed). You can't get around that if you actually need a virtual class.

The question you should be asking yourself is whether you actually need it. Depending on your access methods to this data that might not be the case.

Considering you need virtual inheritance but all common methods that need to be callable from all your classes you can have a virtual base class and use a bit less space than your original design in the following way:

class Base{
public: int id=0;
virtual ~Base();
// virtual void Function();

};
class B : public Base{
public: int fieldB=0;
// void Function() override;
};
class C : public B{
public: int fieldC=0;
};
class D : public B{
public: int fieldD=0;
};
class E : public C, public D{

};

int main (){
std::cout<<"Base="<<sizeof(Base)<<std::endl; //16
std::cout<<"B="<<sizeof(B)<<std::endl; // 16
std::cout<<"C="<<sizeof(C)<<std::endl; // 24
std::cout<<"D="<<sizeof(D)<<std::endl; // 24
std::cout<<"E="<<sizeof(E)<<std::endl; // 48
}

In the case that there are cache misses but the CPU still has power to process the results you can furter decrease the size by using compiler-specific instructions to make the data structure as small as possible (next example works in gcc):

#include<iostream>

class [[gnu::packed]] Base {
public:
int id=0;
virtual ~Base();
virtual void bFunction() { /* do nothing */ };
virtual void cFunction() { /* do nothing */ }
};
class [[gnu::packed]] B : public Base{
public: int fieldB=0;
void bFunction() override { /* implementation */ }
};
class [[gnu::packed]] C : public B{
public: int fieldC=0;
void cFunction() override { /* implementation */ }
};
class [[gnu::packed]] D : public B{
public: int fieldD=0;
};
class [[gnu::packed]] E : public C, public D{

};

int main (){
std::cout<<"Base="<<sizeof(Base)<<std::endl; // 12
std::cout<<"B="<<sizeof(B)<<std::endl; // 16
std::cout<<"C="<<sizeof(C)<<std::endl; // 20
std::cout<<"D="<<sizeof(D)<<std::endl; // 20
std::cout<<"E="<<sizeof(E)<<std::endl; //40
}

saving an additional 8 bytes at the price of possibly some CPU overhead (but if memory is the issue might help).

Additionally if there is really a single function you are calling for each of your classes you should only have that as a single function which you override whenever necessary.

#include<iostream>

class [[gnu::packed]] Base {
public:
virtual ~Base();
virtual void specificFunction() { /* implementation for Base class */ };
int id=0;
};

class [[gnu::packed]] B : public Base{
public:
void specificFunction() override { /* implementation for B class */ }
int fieldB=0;
};

class [[gnu::packed]] C : public B{
public:
void specificFunction() override { /* implementation for C class */ }
int fieldC=0;
};

class [[gnu::packed]] D : public B{
public:
void specificFunction() override { /* implementation for D class */ }
int fieldD=0;
};

class [[gnu::packed]] E : public C, public D{
void specificFunction() override {
// implementation for E class, example:
C::specificFunction();
D::specificFunction();
}
};

This would also allow you to avoid having to figure out what class which object is before calling the appropriate function.

Furthermore, assuming your original virtual class inheritance idea is what works best for your application you could restructure your data so that it's more easily accessible for caching purposes while also decreasing the size of your classes and having your functions accessible at the same time:

#include <iostream>
#include <array>

using namespace std;

struct BaseFields {
int id{0};
};

struct BFields {
int fieldB;
};

struct CFields {
int fieldB;
};

struct DFields {
int fieldB;
};

array<BaseFields, 1024> baseData;
array<BaseFields, 1024> bData;
array<BaseFields, 1024> cData;
array<BaseFields, 1024> dData;

struct indexes {
uint16_t baseIndex; // index where data for Base class is stored in baseData array
uint16_t bIndex; // index where data for B class is stored in bData array
uint16_t cIndex;
uint16_t dIndex;
};

class Base{
indexes data;
};
class B : public virtual Base{
public: void bFunction(){
//do something about "fieldB"
}
};
class C : public virtual B{
public: void cFunction(){
//do something about "fieldC"
}
};
class D : public virtual B{
};
class E : public virtual C, public virtual D{};

int main (){
std::cout<<"Base="<<sizeof(Base)<<std::endl; // 8
std::cout<<"B="<<sizeof(B)<<std::endl; // 16
std::cout<<"C="<<sizeof(C)<<std::endl; // 16
std::cout<<"D="<<sizeof(D)<<std::endl; // 16
std::cout<<"E="<<sizeof(E)<<std::endl; // 24
}

Obviously this is just an example and it assumes you don't have more than 1024 objects at a point, you can increase that number but above 65536 you'd have to use a bigger int to store them, also below 256 you can use uint8_t to store the indexes.

Furthermore if one of the structures above adds very little overhead to it's parent you could reduce the number of arrays you use to store the data, if there's very little difference in the size of objects you can just store all the data in a single structure and have more localized memory accesses. That all depends on your application so I can't give more advice here other than to benchmark what works best for your case.

Have fun and enjoy C++.

How in C++ multiple inheritance choose of which base class method will be inherited?

Disambiguate the call to the function. Take the following example:

class A { virtual void foo(); };
class B { virtual void foo(); };
class C : public A ,public B { void foo(); };

To call either foo from A, B or even from the child class: C do

C *obj = new C;
obj->foo();
obj->A::foo();
obj->B::foo();

Source/inspiration: http://www.cprogramming.com/tutorial/multiple_inheritance.html

diamond inheritance virtual member casting with pointers

Since func is virtual throughout the hierarchy, any direct call to func through a pointer to any of the types involved will call D::func. To do the cast in the code in the question, use dynamic_cast<C*>(p). But that doesn't remove the virtual-ness of func, so that will end up calling D::func, just as p->func() does.

To get rid of the virtual-ness, you have to name the class as well as the function. In a simpler context:

D *d = new D;
d->C::func(); // calls C::func

When you have a pointer to the base type instead of a pointer to the derived type you have to convert the pointer to a type that has C::func. That conversion is done with dynamic_cast, like this:

A *p = new D;
dynamic_cast<C*>(p)->C::func();

Depending on your compiler, you might have to fiddle with your class definitions a bit to get rid of linker errors. Some compilers get confused with inheritance from classes with no non--inline functions.

Confused on C++ multiple inheritance

"multiple inheritance is typically a sign of a bad code design" - parents that are pure interfaces are not counted in regards to this rule. Your I* classes are pure interfaces (only contain pure virtual functions) so you Digital*Point classes are OK in this respect



Related Topics



Leave a reply



Submit