Правильный список инициализаторов при наследовании от виртуального базового класса без создания проблем с ромбами

Итак, я понимаю, что когда вы создаете ромб в наследовании, наиболее производный класс должен явно вызывать конструктор подобъектов виртуального класса в его списке инициализаторов.

Но как насчет классов, которые наследуются от виртуального класса без того, чтобы это наследование создавало сам алмаз? например, Bow наследуется от виртуального базового класса Weapon, нужен ли Bow вызов конструктора Object в его списке инициализаторов и почему?

Я немного запутался со всеми наследованиями классов и списками инициализаторов, и мне просто нужно сначала прояснить ситуацию, прежде чем продолжить, и удалить все ненужные вызовы в моих списках инициализаторов.

Конструктор объекта принимает sf :: Vector2f, который представляет собой два числа с плавающей запятой. До сих пор в качестве виртуальных базовых классов у меня были «Подвижность», «Оружие» и «Снаряд», поскольку они являются частью алмаза.

введите описание изображения здесь

// 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
}

person Yiannis    schedule 20.02.2020    source источник
comment
Покажите свой код (минимальный воспроизводимый пример). Если вы наследуете, вы можете вызвать конструктор базового класса, виртуальный или нет.   -  person 463035818_is_not_a_number    schedule 20.02.2020
comment
Я удалил свой ответ, потому что он был плохим. Это помогло мне выявить брешь в моем понимании, но ничего не помогло в вашей конкретной ситуации. Убедительно прошу вас еще раз предоставить минимальный воспроизводимый пример   -  person 463035818_is_not_a_number    schedule 20.02.2020
comment
Вам необходимо инициализировать каждый прямой или косвенный виртуальный базовый класс, независимо от того, есть ли у него ромб или нет.   -  person n. 1.8e9-where's-my-share m.    schedule 20.02.2020
comment
хорошо, дайте мне немного времени, чтобы написать вам минимальный пример, я подумал, что на это можно ответить без кода   -  person Yiannis    schedule 20.02.2020
comment
мой фактический код класса разделен на файлы заголовков и реализации отдельно   -  person Yiannis    schedule 20.02.2020
comment
Независимо от того, найдете вы свой ответ или нет, подобные сложные иерархии множественного наследования вызовут головную боль. Учитывая, что ваш пример, вероятно, является небольшим фрагментом вашей игры, я рекомендую рассмотреть такой шаблон, как Entity-Component-System (ECS), чтобы упростить ваш код путем разделения данных, возможностей и взаимодействий.   -  person parktomatomi    schedule 21.02.2020


Ответы (1)


Итак, после некоторого исследования, полезных комментариев, экспериментов и наведения порядка в моем коде я понял ответ и понял, что пошло не так. Я поставлял конструктору объекта аргумент по умолчанию, поэтому он вызывался таким образом, независимо от того, включил ли я вызов в список инициализаторов класса или нет.

Класс, наследующий от виртуального базового класса , должен включать вызовы конструкторов подобъектов виртуального базового класса в свой список инициализаторов, если они не имеют конструкторов по умолчанию.

Итак, в моем примере, поскольку Bow наследуется от виртуального базового класса Weapon, который наследуется от Object, его список инициализаторов будет выглядеть так:

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

Для полноты я добавляю, какими должны быть списки инициализаторов Grenade, когда нет конструктора по умолчанию:

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::Grenade() : Object(GRENADE_STARTING_POS), Projectile(GRENADE_STARTING_POS, GRENADE_DAMAGE), Weapon(GRENADE_SHOT_DELAY, GRENADE_STEALTH)
{
  //constructor code
}
person Yiannis    schedule 05.04.2020