2013-10-12 2 views
4

У меня есть класс Enemy, который я хочу быть базовым классом для всех типов врагов, а также чистым абстрактным. На этом этапе все его члены и методы должны делиться производными классами. В частности, существует метод loadTexture, который использует статический элемент texture.Наследование со статическими методами/полями C++

class Enemy 
    { 
     int hp; 
     int damage; 
     // 
     // all other fields 
     // 
     static TextureClass* texture; //needs to be static because every instance 
             //of enemy uses the same texture 
    public: 
     static void loadTexture() 
     { 
      CreateTextureFromFile("somefilepath",&texture); //needs to be static 
      // because textures are loaded before any instance is crated 
     } 
     void draw() 
     { 
      DrawToScreen(&texture, ...many data members passed here...); 
     } 
    }; 

    TextureClass* Enemy::texture = nullptr; 

Теперь, если я хотел бы сделать Enemy абстрактные и создавать различные типы врагов, очевидный выбор будет наследование. Поэтому я создаю:

class EnemyA : Enemy 
{ 
    static TextureClass* texture; 
}; 

class EnemyB : Enemy 
{ 
    static TextureClass* texture; 
}; 

Но теперь, как загрузить текстуру для каждого из них? Я не могу использовать loadTexture, определенный в базе, очевидно. Таким образом, единственный выбор, помимо написания одного и того же метода X раз (где X - количество типов врагов), заключается в удалении loadTexture из базы и создании глобальной функции, не так ли? То же самое касается draw, я должен был бы пересмотреть его каждый полученный, несмотря на то, что это будет выглядеть точно так же ...

TL; Д.Р. loadTexture и draw имеют точно такое же тело, независимо от типа противника, но они используют статическое поле, которое отличается в каждом из них. Есть ли способ определить что-то вроде единого метода, что при вызове в производном классе будет использовать поле из производного, а не базы?

Благодарим за любые ответы.

+1

Создайте виртуальный метод 'TextureClass * getTexture()', который все подклассы реализуют для извлечения указателя текстуры, а общие методы используют для получения указателя текстуры. – mah

+1

Вы можете сделать это с помощью [CRTP] (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern), если это необходимо, чтобы метод был статическим. В противном случае предпочтительным будет нестатический виртуальный метод. – WhozCraig

+0

@mah 'common method' означает «глобальная функция»? –

ответ

5

Как отметил BЈовић, если вы хотите использовать наследование, чтобы иметь LoadTexture() и рисовать() методы ссылки на правильную текстуру для вашего класса, ответ, вероятно, заключается в использовании CRTP реализовать свой класс Enemy, как следует:

template<typename TDerivedEnemy> 
class Enemy 
{ 
    int hp; 
    int damage; 
    // 
    // all other fields 
    // 
    static TextureClass* texture; //needs to be static because every instance 
            //of enemy uses the same texture 
public: 
    static void loadTexture() 
    { 
     CreateTextureFromFile("somefilepath",&texture); //needs to be static 
     // because textures are loaded before any instance is crated 
    } 
    void draw() 
    { 
     DrawToScreen(&texture, ...many data members passed here...); 
    } 
}; 

как только это будет сделано, вы можете объявить ваши конкретные классы врагов нравится следующим образом:

class EnemyA : public Enemy<EnemyA> 
{ 
public: 
    typedef Enemy<EnemyA> tBase; 
    ... 
}; 

class EnemyB : public Enemy<EnemyB> 
{ 
public: 
    typedef Enemy<EnemyB> tBase; 
    ... 
}; 

затем, в файле реализации, также необходимо определить статические переменные как для EnemyA и EnemyB:

TextureClass* EnemyA::tBase::texture = nullptr; 
TextureClass* EnemyB::tBase::texture = nullptr; 

Название «Любопытно Периодическая шаблона шаблон» исходит из того, что EnemyA и EnemyB унаследовать от шаблона класс, где они уже являются шаблонами параметров, поэтому рекурсия.

Edit: как описано в комментарии, этот подход приводит к EnemyA и EnemyB не имеющие общий базовый класс, что делает невозможным ссылаться на них в едином порядке, то есть вы не можете объявить, например, в

std::vector< EnemyA* OR EnemyB* ??? > enemies; 

потому что просто нет общего базового класса. Чтобы обойти эту проблему, вы можете объявить общий абстрактный базовый класс, как следует:

class EnemyBase { 
public: 
    virtual void draw() = 0; 
} 

затем сделать вашу реализацию шаблона наследуют от него:

template<typename TDerivedEnemy> 
class Enemy : public EnemyBase 
{ 
    ... 
}; 

Что позволяет сделать это:

std::vector<EnemyBase*> enemies; 

//fill the enemies vector 
... 

for (auto enemy : enemies) 
{ 
    enemy->draw(); 
} 
+0

Итак, при вызове 'objectOfEnemyA.Draw()' будет использовать текстуру из подкласса, а не базы, даже если я не определяю ее в 'enemyA'? Кроме того, как я могу справиться со статической «текстурой нагрузки»? Я не могу назвать «EnemyA :: loadTexture()», правильно? Разве лучше не создавать глобальную функцию, которая берет текстурный адрес и загружает его? –

+0

Вам не нужно определять текстуру в классе EnemyA, так как она уже определена в базовом классе EnemyA: Enemy . Большинство компиляторов должны позволить вам напрямую вызвать EnemyA :: loadTexture (...), но на некоторых старых компиляторах вам может потребоваться вместо EnemyA :: tBase :: loadTexture (...). Одна из точек CRTP, которая делает ее полезной для создания, например, базовых классов шаблонов Singleton, заключается в том, что она позволяет эффективно «наследовать статические вещи», создавая процесс создания шаблона для создания статических элементов для вас. –

+0

А, я думаю, я понимаю это сейчас: уроки, полученные таким образом, не будут разделять одну и ту же текстуру, потому что они не происходят из «Врага», но ... «Враг », да? Так на самом деле его вроде как они не происходят от того же «Врага»? –

1

Есть несколько способов:

  1. делают EnemyX шаблон, который имеет статическую переменную текстуры. Вы можете использовать CRTP для этого
  2. передать объект текстуры во вражеские объекты (какой-то IOC) - либо для конструктора, либо для метода. В этом случае текстура не будет статичной, но это будет указывать на объект текстуры (указатель или ссылку)
Смежные вопросы