Я работаю над игрой, в которой есть монстры и драконы. Драконы могут делать все, что могут делать монстры, кроме того, что они могут дышать огнем.
Я создал класс типа «монстр» и класс, который наследуется от монстра под названием «дракон».
Затем у меня есть класс под названием «монстры», который имеет в качестве частного члена вектор, который будет содержать элементы дракона и монстра.
В основном игровом цикле мне нужно циклически перемещаться по вектору и пускать огонь, если текущий элемент — дракон, и ничего не делать, если он просто монстр.
Я пытался использовать typeid (), но он всегда возвращает monster *, является ли текущий элемент простым монстром или драконом.
Есть ли способ сделать это или нет смысла даже использовать наследование в этом случае?
Будет ли для драконов больше смысла не наследовать, а вместо этого оставаться независимым от монстра?
Любой совет приветствуется.
Вы мог сделать что-то подобное
class Monster {};
class Dragon : public Monster
{
public:
void BlowFire() {}
}
std::vector<Monster*> monsters; // Collection of monsters
for(auto it = monsters.begin(); it != monsters.end(); ++it)
{
Dragon* pDragon = dynamic_cast<Dragon*>(*it); // Attempt to convert pointer
if(pDragon)
{
pDragon->BlowFire(); // If it is indeed a Dragon, blow fire.
}
}
Но звучит так, будто вы должны организовать свои занятия немного лучше. Почему бы вам не иметь все свои классы монстров Attack
метод? Таким образом, вы можете переопределение методы для каждого типа монстров.
class Monster
{
public:
virtual void Attack() {} // All monsters can attack
};
class Dragon : public Monster
{
public:
virtual void Attack() { // Do something special for a Dragon. }
};
Monster myMonster;
Dragon myDragon;
myMonster.Attack(); // Generic monster attack.
myDragon.Attack(); // Special super dragon fire attack!
Есть два возможных подхода: использовать виртуальную функцию или использовать dynamic_cast
, Если monster
имеет виртуальную функцию blow_fire()
это ничего не делает, dragon
может переопределить это, чтобы сделать все, что подходит для дракона. Тогда вы просто позвоните для всех monster
объекты, и драконы будут дуть огонь. С помощью dynamic_cast
код будет что-то вроде этого:
if (dragon *dragon_ptr = dynamic_cast<dragon*>(monster_ptr)) {
dragon_ptr->blow_fire();
}
Обычно это считается плохим подходом, потому что он не растет естественным образом. Если вы добавили монстра другого типа, который может взорвать огонь, вам придется добавить еще одного dynamic_cast
, С виртуальной функцией новый тип просто переопределит виртуальную функцию.
Не то, чтобы это был лучший способ сделать это, но просто для вашей информации, вы звонили typeid
на указатель который не работает, если вы хотите тип времени выполнения объекта. typeid
делает работать во время выполнения, когда это необходимо, но вам нужно разыменовать указатели на полиморфные типы:
typeid(*monsterpointer)
И это получит тип во время выполнения *monsterpointer
будь то monster
или же dragon
,
Тем не мение, typeid
только для точных сравнений, поэтому, если вы добавите еще один класс с именем ImpairedDragon
что наследует от Dragon
, typeid
код сломается и не будет работать с ними. Так что вы, вероятно, должны использовать dynamic_cast
которая работает с наследованием или, что еще лучше, с виртуальной функцией, если вы можете придумать, как правильно ее использовать.
Если в вашем контейнере есть указатели на монстров, то общедоступные члены-монстры — это все, что вы можете получить с помощью этих указателей безопасным для типов способом (из моей памяти). Вы можете попробовать прибегнуть к дизайну на основе интерфейса, например, COM, чтобы можно было запросить у базового класса монстров какие-либо дополнительные функции, например интерфейсы. Я бы предоставил вам образец, но мой обеденный перерыв закончился.
Как насчет размещения пустого (без операции) виртуального blowFire
в базовом классе, а затем переопределить его с чем-то в производном классе?
Было бы еще лучше переименовать метод в specialAttack
или похожее родовое имя, поэтому вам не нужно добавлять похожие методы для других видов монстров.