【问题标题】:Problem about templates and virtual function关于模板和虚函数的问题
【发布时间】:2020-05-07 19:15:56
【问题描述】:

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

首先,我有一个名为Unit 的父类 和 2 个名为 HeroMonster 的子类。 这 2 个类都有 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];

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

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

请帮帮我!

【问题讨论】:

  • 除您描述的以外,在您显示的代码中 Hero 不是从 Unit 派生的。
  • 我编辑了它,感谢您的提醒:)
  • 那么,如果我理解正确的话,你想在通用单元上调用 Attack(),将通用单元作为目标传递给它,并让子类覆盖 Attack() 的实现?
  • 您说unitVector 在您的评论中包含英雄和怪物,但事实并非如此。它只包含Units。阅读object slicing
  • 它实际上有我没有写过的怪物对象,因为我认为它与我的问题无关。即使我有怪物对象我仍然无法使用攻击功能,因为Unit中没有这样的功能。

标签: c++ templates inheritance virtual-functions


【解决方案1】:

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

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

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

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

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

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

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 没有任何子类 - 如果有,请确保析构函数和 Attack() 都是虚拟的。

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


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

...

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

这里的主要区别是指针的使用。 C++ 中的多态性基本上要求您使用指针 - 使用没有指针的普通对象将导致编译器“切片”您的类,从而删除您的任何自定义行为。这将成为一个主要问题,例如,您将一个神圣修饰符添加为 Crusader 类的成员,然后尝试从“切片”类访问它 - 程序将立即崩溃,因为“切片”删除了任何不在基类中的内容从对象。

为了让它们使用起来更“好”一些,我在std::shared_ptr 中封装了指针。当没有更多引用时,这提供了自动引用计数和销毁。但是,无论您使用的是 std::shared_ptr 还是原始指针,都适用相同的规则。

【讨论】:

  • 模板多态,而不是动态多态!
  • “Ackshually”只有模板元编程是编译时多态性 - 模板本身是通用编程,在没有元编程的语言中,它们最终就是这样。
  • 泛型是多态性,不仅仅是模板元编程。引用:“使用参数多态性,可以通用地编写函数或数据类型,以便它可以相同地处理值而不依赖于它们的类型。这样的函数和数据类型分别称为泛型函数和泛型数据类型并形成泛型编程的基础。”
【解决方案2】:

您的代码应如下所示:

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

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

这里,你要创建一个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 继承(您可以通过将override替换 为@987654332 @)。

【讨论】:

    猜你喜欢
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多