Проблема с шаблонами и виртуальной функцией - PullRequest
1 голос
/ 07 мая 2020

У меня проблема с шаблонами - несовпадение виртуальных функций.

Прежде всего, у меня есть родительский класс с именем Unit и 2 дочерних класса с именами Hero и Monster. Эти 2 класса имеют два подкласса, и один из подклассов класса Hero - Crusader.

class Unit {
  template <typename target>
  virtual void  Attack(typename std::vector<target>::iterator targetPtr, std::shared_ptr<AttackSkill> skillPtr) {} 
  //ERROR (template function can not be virtual)
};

class Hero : public Unit {
  template <typename target>
  virtual void  Attack(typename std::vector<target>::iterator targetPtr, std::shared_ptr<AttackSkill> skillPtr) {}
  // ERROR (template function can not be virtual)
};

class Crusader : public Hero {
  template <typename target>
  void Attack(std::vector<target>::iterator targetPtr, std::shared_ptr<AttackSkill> skillPtr) {}
};


// Unit vector that contains heroes and monsters 
std::vector<Unit> unitVector;

Crusader crusader1; 
unitVector.at(0).emplace_back[crusader1];

Проблема в том, что я хочу получить доступ к функции Attack из unitVector[0], написав virtual void Attack functions в класс Hero и класс Unit, но, к сожалению, C ++ не позволяет этого. Что мне делать, чтобы написать эти виртуальные функции? Или я могу использовать некоторые общие c указатели (если они есть) вместо шаблонов?

Я хочу использовать функцию атаки от разных монстров и разных героев, поэтому мне нужно использовать шаблоны. Если я попытаюсь написать разные функции, такие как attackHero и attackMonster, одна из них не будет работать, поскольку, когда я это делаю, я в основном пытаюсь взять итератор некоторого вектора несуществующего класса.

Пожалуйста, помогите мне!

Ответы [ 2 ]

1 голос
/ 07 мая 2020

Шаблоны здесь не тот инструмент. Полиморфизм.

Шаблоны полезны, когда у вас есть похожий или идентичный код, который должен работать с несколькими несвязанными типами. Полиморфизм полезен, когда у вас есть набор напрямую связанных типов, каждый из которых должен делать разные вещи.

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

Итак, с теорией все в порядке, но нас больше интересует реализация, а не теория ...

Чтобы решить проблему, вам нужно сделать два исправления. делать.

Во-первых, определения классов неверны в том виде, в каком они написаны - нам не нужны какие-либо шаблоны, чтобы мы могли избавиться от всего этого.

class Unit
{
public:
    Unit();
    virtual ~Unit();

    virtual void Attack(std::shared_ptr<Unit> Target, const AttackSkill& skill);

};

...

class Hero : public Unit
{
public:
    Hero();
    virtual ~Hero();

    virtual void Attack(std::shared_ptr<Unit> Target, const AttackSkill& Skill) override;

};

...

class Crusader : public Hero
{
public:
    Crusader();
    ~Crusader();

    void Attack(std::shared_ptr<Unit> Target, const AttackSkill& Skill);

};

Здесь я ' m при условии, что у Crusader нет подклассов - если есть, убедитесь, что деструктор и Attack() оба виртуальны.

Теперь второе важное изменение - это ваши методы сбора и вставки:


std::vector<std::shared_ptr<Unit>> Units;

...

Units.emplace_back(std::make_shared(new Crusader());

Основное отличие здесь - использование указателей. Полиморфизм в C ++ в основном требует, чтобы вы использовали указатели - использование простых объектов без указателей заставит компилятор "разрезать" ваш класс, удалив любое ваше настраиваемое поведение. Это становится серьезной проблемой, если, скажем, вы добавляете модификатор святости в качестве члена класса Crusader, а затем пытаетесь получить к нему доступ из «нарезанного» класса - программа немедленно взломает sh, потому что «нарезка» удаляет все, чего нет в базовый класс объекта.

Чтобы сделать их немного «удобнее», я заключил указатели здесь в std::shared_ptr. Это обеспечивает автоматический c подсчет и уничтожение ссылок, когда ссылок больше нет. Однако те же правила применяются независимо от того, используете ли вы std::shared_ptr или необработанные указатели.

1 голос
/ 07 мая 2020

Ваш код должен выглядеть так:

class IUnit
{
public:
    virtual void attack(IUnit& target, const AttackSkill& skill) = 0;
};

class Hero : public IUnit
{
public:
    virtual void attack(IUnit& target, const AttackSkill& skill) override { /* do something */ }
};

class Crusader final : public Hero 
{
public:
    void attack(IUnit& target, const AttackSkill& skill) final { /* do something else */}
};

Что происходит, так это то, что шаблоны функций определяются в времени компиляции ( stati c полиморфизм ), и мы используем виртуальные функции, чтобы определить, какая функция должна быть вызвана время выполнения ( динамический c полиморфизм ), таким образом , невозможно совместить оба. Вы должны прочитать этот вопрос для получения дополнительной информации

Здесь вы хотите создать функцию attack(target, skill), которая использует предоставленный навык в соответствии с атакующим юнитом (обычно интерфейс обозначается как I), и полученный навык атаки. Он может работать с любым типом IUnit, и каждый IUnit должен его реализовывать.

Предположим, что мы реализовали класс monster, мы можем использовать атаку следующим образом :

class Monster : public IUnit
{
public:
    virtual void attack(IUnit& target, const AttackSkill& skill) override { /* placeholder */ }
};


int main()
{
    Monster some_monster{};
    Crusader some_crusader{};

    some_crusader.attack(some_monster, AttackSkill{});
}

Несколько небольших замечаний:

  1. Используйте ключевое слово override, чтобы сообщить компилятору, что он действительно правильно переопределяет виртуальную функцию, это может сэкономить вам много отладки!
  2. Используйте ключевое слово final, чтобы сообщить компилятору, что никакой другой класс не должен наследовать от класса Crusader (вы можете сделать это для определенной функции c, заменив 1041 * с final).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...