Search code examples
c++inheritancemultiple-inheritancebase-classvirtual-inheritance

Correct initializer list when inheriting from virtual base class without creating diamond problem


So I get that when you create a diamond in inheritance the most derived class needs to explicitly call the constructor of the virtual class's sub-objects in its initializer list.

But what about classes that inherit from a virtual class without that inheritance creating a diamond itself? e.g. Bow inherits from virtual base class Weapon, does Bow need a call to Object's constructor in its initializer list too and why?

I've become a bit tangled with all the classes inheritances and initializer lists and I just need to clear things up first before continuing and remove any unnecessary calls in my initializer lists.

Object's constructor takes a sf::Vector2f which is two floats. So far I've had Movable, Weapon and Projectile as virtual base classes since they are part of the diamond.

enter image description here

// sf::Vector2f is an SFML data type which consists of two floats

class Object
{
private:
    sf::Vector2f m_pos;
public:
    Object(sf::Vector2f start_pos) {m_pos = start_pos;};
}

class Movable : virtual public Object
{
public:
    Movable(sf::Vector2f start_pos) : Object(start_pos) { ... };
}

class Weapon : virtual public Object
{
public:
    Weapon(float shotDelay, bool isStealth) : Object(sf::Vector2f(0,0)) { ... };
}

class Projectile : public Movable
{
public:
    Projectile (sf::Vector2f startPos, int damage) : Movable(startPos) { ... };
}

class Bow : public Weapon
{
public:
    Bow() : Weapon(BOW_SHOT_DELAY, BOW_STEALTH) { ... };
}

class Grenade : public Weapon, public Projectile
{
public:
    Grenade() : Weapon(GRENADE_SHOT_DELAY, GRENADE_STEALTH) {};//for Weapon
    Grenade(sf::Vector2f startPos) : Projectile(startPos, GRENADE_DAMAGE);//for Projectile
}

Solution

  • So after some research, the helpful comments, playing around and tidying up my code I figured out the answer and what was going wrong. I was supplying the Object constructor with a default argument so it was being called that way whether I included the call in the initializer list of a class or not.

    A class that inherits from a virtual base class must include calls the constructors of the virtual base class's sub-objects in its initializer list if they do not have default constructors.

    So in my example, since Bow inherits from virtual base class Weapon which inherits from Object, its initializer list will look like this:

    Bow::Bow() : Object(BOW_STARTING_POS), Weapon(BOW_SHOT_DELAY, BOW_STEALTH)
    {
      //constructor code
    }
    

    For completeness I'm adding what the Grenade initializer lists should be when there is no default constructor:

    Grenade::Grenade() : Object(GRENADE_STARTING_POS), Weapon(GRENADE_SHOT_DELAY, GRENADE_STEALTH)
    {
      //weapon constructor code
    }
    
    Grenade::Grenade() : Object(GRENADE_STARTING_POS), Projectile(GRENADE_STARTING_POS, GRENADE_DAMAGE)
    {
      // projectile constructor code
    }
    

    P.S. grenade is split into two constructors because my design required it that way. In a different case where you only need one constructor for a class inheriting from two virtual base classes, one initializer list can contain all the constructor calls as such:

    Grenade::Grenade() : Object(GRENADE_STARTING_POS), Projectile(GRENADE_STARTING_POS, GRENADE_DAMAGE), Weapon(GRENADE_SHOT_DELAY, GRENADE_STEALTH)
    {
      //constructor code
    }