c ++中的多级继承(CRTP)

时间:2014-03-14 03:44:09

标签: c++ crtp

请帮我解决这个问题。 WhiteDragon将调用Dragon::attacks()而不是MonsterImplement::attacks()  std::cout << monster->numAttacks << std::endl;,这里存在歧义错误。如果我改变龙  从MonsterImplement派生,然后就行了 numAttacks不会编译,因为Dragon没有WhiteDragon数据成员(也不应该,因为不同类型的Dragons具有不同的值)。因此,我需要Dragon::attacks()来调用finalizeMonster()并在实例化期间调用#include <iostream> struct Monster { virtual void finalizeMonster() {} virtual void attack() {} }; template <class MONSTER, int NUM> struct MonsterInt: virtual public Monster { static int numAttacks; }; template <class MONSTER, int NUM> int MonsterInt<MONSTER, NUM>::numAttacks = NUM; template <class BASE, class MONSTER> struct MonsterImplement: virtual public BASE { MonsterImplement() {finalizeMonster();} virtual void finalizeMonster() override; virtual void attack() override {std::cout << "MonsterImplement::attack()" << std::endl;} }; struct Dragon: public Monster { // or Dragon: public MonsterImplement<Monster, Dragon> ? // but then Dragon will also call the MonsterImplement constructor (when it has no numAttacks member) virtual void attack() override {std::cout << "Dragon::attack()" << std::endl;} }; struct WhiteDragon: public MonsterInt<WhiteDragon, 3>, public MonsterImplement<Dragon, WhiteDragon> { WhiteDragon(): MonsterImplement<Dragon, WhiteDragon>() {} }; template <class BASE, class MONSTER> inline void MonsterImplement<BASE, MONSTER>::finalizeMonster() { MONSTER* monster = static_cast<MONSTER*> (this); std::cout << monster->numAttacks << std::endl; } int main() { WhiteDragon wd; wd.attack(); } 。如果我使Dragon虚拟派生类Monster,WhiteDragon会调用MonsterImplement :: attacks()。

{{1}}

2 个答案:

答案 0 :(得分:3)


(复制自之前的评论。)

观点#1

CRTP旨在提供非动态行为。如果“numAttacks”的值随每个派生类而变化,则这不是“非动态”情况。反例是将非静态非虚方法int numAttacks() { return 3; }放在派生类中,然后在CRTP基类中添加一些方法(在所有派生类之间共享的攻击逻辑),然后可以在其派生类上调用numAttacks()方法,而不会产生虚函数调用。

示例:

struct Monster
{
    virtual void attack() = 0;
    virtual int getNumAttacks() const = 0;
};

template <struct MONSTER>
struct AttackLogic : virtual public Monster
{
    virtual void attack() override
    {
        /* allowed to call MONSTER::getNumAttacks(), renamed to avoid confusion. */

        int numAttacks = static_cast<MONSTER*>(this).getNumAttacks();

        /* Use the value in attack calculations. */
    }
};

struct Unicorn 
    : virtual public Monster
    , virtual public AttackLogic<Unicorn>
{
    virtual int getNumAttacks() const override
    {
        return 42; // Unicorn is awesome
    }
};

免责声明:代码仅用于解释我的建议。不适合实际使用。没有用编译器测试过。我对虚拟遗传的了解很少,因此上面的示例代码中可能存在错误或违反准则。


您当前的继承链是:(位于顶部)

  • Monster
    • Dragon
      • MonsterImplement<Dragon, WhiteDragon>
        • WhiteDragon

Monster定义:

  • 虚拟finalizeMonster() // abstract
  • 虚拟attack() // abstract

Dragon定义:

  • virtual attack() //具体,覆盖Monster.attack()

MonsterImplement<...>定义:

  • virtual attack() //具体,覆盖Dragon.attack()和Monster.attack()

WhiteDragon定义:

  • (未定义新的虚拟方法)

很明显“修复bug后”会调用MonsterImplement.attack(),因为它是Dragon的子类,因此会覆盖它。

一般来说,它只表示当前的继承层次结构设计糟糕,并且没有人能够修复它。


透视#2

通过CRTP模式注入静态int很少值得付出努力。 CRTP更适合以不会被覆盖的方式注入一组非静态非虚方法(“样板”),这样可以避免每个派生类重新实现相同的“样板”。

至少将静态int numAttacks转换为虚函数

virtual int numAttacks() const { throw std::exception(); }

virtual int numAttacks() const = 0;  // abstract

然后在WhiteDragon中提供具体实现以返回3.

struct WhiteDragon : ... 
{   ... 
    virtual int numAttacks() const override { return 3; } 
    ... 
};

答案 1 :(得分:0)

template <class MONSTER, int NUM>
struct MonsterInt: virtual public Monster {
    static int numAttacks;
   };

这门课的目的是什么?它似乎只是给了一个类一些攻击,在这种情况下,从怪物派生出来并没有多大意义。

template <int NUM>
struct MonsterInt {
    static int numAttacks;
   };

那&#39;修复&#39;我认为这个程序,但很难真正说出来,因为很难从你的代码中得到意图。