关于模板和虚函数的问题

Problem about templates and virtual function

我有模板问题 - 虚函数不匹配。

首先,我有一个 parent class 名为 Unit 和 2 child class 名为 HeroMonster。 这 2 classes 有 2 个 subclasses 并且 class Hero 的 subclass 之一是 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];

问题是,我想通过将 virtual void Attack 函数写入 Hero class 和 Unit 来从 unitVector[0] 访问 Attack 函数class 但不幸的是 C++ 不允许这样做。 怎么写这些虚函数呢? 或者我可以使用一些通用指针(如果有这样的东西)而不是模板吗?

我想使用来自不同怪物和不同英雄的攻击功能,所以我必须使用模板。如果我尝试编写不同的函数,例如 attackHeroattackMonster,其中一个不起作用,因为当我这样做时,我基本上是在尝试获取一些 non-existent [= 的迭代器34=]的向量。

请帮帮我!

您的代码应如下所示:

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

函数模板是在编译时确定的(静态多态性) ,并且我们使用虚函数来确定应该在 运行-time 上调用哪个函数(dynamic多态性),因此,不可能将两者结合起来。你应该阅读 this question for more information

这里,你想创建一个attack(target, skill)函数,根据攻击单位(一般接口用I表示),赋予使用的攻击技能,使用提供的技能。它可以对任何类型的IUnit进行操作,每个IUnit都应该实现它。

假设我们实施了 class 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 告诉编译器没有其他 class 应该从 class Crusader 继承(您可以通过 替换 overridefinal).

模板不是您在这里需要的工具。多态性是。

当您有相似或相同的代码必须处理多个不相关的类型时,模板很有用。当您有一组直接相关的类型都需要做不同的事情时,多态性很有用。

在这里,你有圣教军、英雄和怪物,它们攻击、受到伤害和移动的方式都不同,但它们都是不同的单位,这意味着它们都直接相关,属于第二类.

现在,理论很好,但我们更关心的是实施而不是理论...

要解决此问题,您需要进行两个修复。

首先,class 定义不正确 - 我们不需要任何模板内容,因此我们可以摆脱所有这些。

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);

};

在这里,我假设 Crusader 没有任何子classes - 如果有,请确保析构函数和 Attack() 都是虚拟的。

现在,第二个主要变化是您的收集和插入方法:


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

...

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

这里的主要区别在于指针的使用。 C++ 中的多态性基本上要求您使用指针 - 使用不带指针的普通对象将导致编译器 "slice" 您的 class,删除您的任何自定义行为。如果你添加一个圣洁修饰符作为十字军 class 的成员,然后尝试从 "sliced" class 访问它,这将成为一个主要问题 - 程序将立即崩溃,因为"slicing" 从对象中删除不在基础 class 中的任何内容。

为了让它们有点 "nicer" 使用,我将这里的指针包装在 std::shared_ptr 中。当没有更多引用时,这提供了自动引用计数和销毁。但是,无论您使用 std::shared_ptr 还是原始指针,都适用相同的规则。