在这个程序中,我试图实现的最终目标是以简单的文本模拟器的形式模拟一个名为"Fire Emblem“的游戏。这里的主要功能是通过一个名为Skill的抽象类,然后由定义具有独特效果的特定技能的更具体的子类继承。不幸的是,当程序运行时,尽管Unit是已经定义的对象类型,但在Skill.h .h文件的激活函数中生成了上述错误。我怀疑这可能与头文件的#include顺序有关,但我认为没有其他方法可以改变顺序并破坏功能。有没有一种方法可以避免重新包含.h文件,同时保持我程序中当前拥有的逻辑?另外,我为大量的代码道歉,我不确定要省略哪些部分。
Skill.h
这个类的目的是作为一个抽象类,更具体的技能子类将从这个抽象类继承并实现activate()函数。
#ifndef SKILL_H
#define SKILL_H
#include "Unit.h"
#include <string>
#include <random>
class Skill{
public:
Skill(const std::string &skillName);
void setSkillLikelyHood(int chance);
int getLikelyHood() const;
std::string getSkillName() const;
//this function generates the error messages specified in the title
virtual void activate(const Unit &player, Unit &enemy ) = 0;
protected:
int random(int min, int max);
int likelyHood;
std::string skillName;
};
#endif // !1Skill.cpp
#include "Skill.h"
Skill::Skill(const std::string &skillName) : skillName(skillName) {
}
void Skill::setSkillLikelyHood(int chance){
likelyHood = chance;
}
int Skill::getLikelyHood() const{
return likelyHood;
}
std::string Skill::getSkillName() const{
return skillName;
}
int Skill::random(int min, int max){
std::random_device seed;
std::default_random_engine rnGen(seed());
std::uniform_int_distribution<int> dist(min, max);
return dist(rnGen);
}Astra.h
#ifndef ASTRA_H
#define ASTRA_H
#include "Skill.h"
class Astra : public Skill{
public:
Astra();
void activate(const Unit &player, Unit &enemy) override;
};
#endifAstra.cpp
#include "Astra.h"
Astra::Astra() : Skill("Astra"){
}
void Astra::activate(const Unit &player, Unit &enemy){
std::cout << player.getName() << " activated " << skillName << " Oh no!\n";
int damage = player.getDamageDealtToEnemy(enemy);
for (size_t i = 0; i < 5; i++){
//if the player lands a hit AND lands a crit, perform the crit against the enemy.
if (player.calculateHit(enemy) && player.calculateCriticalHit(enemy)) {
std::cout << player.getName() << " landed a crit! " << enemy.getName() << " took " << damage << " damage!\n" ;
enemy.getHp() -= damage;
}
//if the player managed to land a hit, but failed to crit, simply land a regular hit.
else if (player.calculateHit(enemy)) {
std::cout << player.getName << " dealt " << damage << " damage!\n";
enemy.getHp() -= damage;
}
//otherwise, the player will miss their attack.
else
std::cout << "Attack missed!\n";
}
}Unit.h
#ifndef UNIT_H
#define UNIT_H
#include "Astra.h"
#include "Weapon.h"
#include <array>
#include <random>
#include <iostream>
#include <memory>
class Unit{
public:
Unit(const std::string &name, const Weapon &weapon, const Skill &s);
~Unit();
void levelUp();
int &getHp();
void attack(Unit &unit);
std::string getName() const;
int getDamageDealtToEnemy(const Unit &enemy) const;
bool calculateHit(const Unit &enemy) const;
bool calculateCriticalHit(const Unit &enemy) const;
friend std::ostream &operator << (std::ostream &os, const Unit &u);
private:
int random(int min, int max) const;
void calculateStats();
std::string name;
std::array<int, 7> stats;
std::array<int, 7> growthRates;
std::unique_ptr<Skill> skill;
Weapon weapon;
int level, hp, avoid, criticalAvoid, hitRate, totalAttack, criticalRate, criticalChance, exp;
static int turnNumber;
};
#endifUnit.cpp
#include "Unit.h"
int Unit::turnNumber = 1;
Unit::Unit(const std::string &name, const Weapon &weapon, const Skill &s) : name(name), weapon(weapon), hp(100), level(1), exp(0),
skill(std::make_unique<Skill>()){
for (int i = 0; i < stats.size(); i++) {
stats[i] = random(20, 25);
growthRates[i] = random(55, 80);
}
calculateStats();
skill->setSkillLikelyHood(stats[2] / 2);
}
Unit::~Unit()
{
}
void Unit::levelUp(){
hp += random(15, 20);
for (int i = 0; i < stats.size(); i++){
if (random(0, 100) < growthRates[i])
stats[i] += random(2, 4);
}
}
int &Unit::getHp(){
return hp;
}
//This function
void Unit::attack(Unit &enemy){
std::cout << "turn: " << turnNumber << "\n";
std::cout << name << " attacked " << enemy.name << "!\n";
//On the first turn, and only the first turn (as doing so after the first turn is unnecessary) calculate the player and the
//enemy's stats after factoring in the weapon triangle bonus.
if (turnNumber == 1) {
weapon.determineWeaponTriangleBonus(enemy.weapon.getWeaponType());
enemy.weapon.determineWeaponTriangleBonus(this -> weapon.getWeaponType());
calculateStats();
enemy.calculateStats();
}
switch (calculateHit(enemy)){
int damage;
//if you manage to land a hit....
case true:
//when you land a critical hit, you will deal three times normal damage!
damage = (calculateCriticalHit(enemy)) ? (totalAttack - enemy.stats[5]) * 3 : (totalAttack - enemy.stats[5]);
std::cout << name << "'s attack: " << totalAttack << "\n";
enemy.hp -= damage;
std::cout << enemy.name << " took " << damage << " damage! HP: " << enemy.hp << "\n\n";
break;
case false:
std::cout << name << " miss his attack! :(\n\n";
}
//base case. When either the player or enemy has died, end the recursive call
if (enemy.hp <= 0) {
std::cout << enemy.name << " is dead!\n";
return;
}
turnNumber++;
enemy.attack(*this);
}
std::string Unit::getName() const{
return name;
}
int Unit::getDamageDealtToEnemy(const Unit &enemy) const{
return totalAttack - enemy.stats[5];
}
int Unit::random(int min, int max) const{
std::random_device seed;
std::default_random_engine rnGen(seed());
std::uniform_int_distribution<int> dist(min, max);
return dist(rnGen);
}
void Unit::calculateStats(){
int &att = stats[0], &mag = stats[1], &skl = stats[2], &spd = stats[3], &lck = stats[4], &def = stats[5],
&res = stats[6];
avoid = (spd * 2) + lck;
criticalAvoid = lck / 2;
hitRate = weapon.getAccuracy() + (skl * 2) + (lck / 2);
criticalRate = skl + weapon.getCritical();
totalAttack = att + weapon.getMight();
}
bool Unit::calculateHit(const Unit &enemy)const{
std::cout << "hit rate: " << hitRate - enemy.avoid << "%\n";
return ((random(0, 100) + random(0, 100)) / 2) < hitRate - enemy.avoid;
}
bool Unit::calculateCriticalHit(const Unit &enemy) const{
//std::cout << "critical hit: " << criticalRate - unit.criticalAvoid << "%\n";
return random(0, 100) < criticalRate - enemy.criticalAvoid;
}
std::ostream & operator<<(std::ostream & os, const Unit & u){
std::array<std::string, 7> statNames{"att", "mag", "skl", "spd", "lck", "def", "res"};
std::cout << u.name << "'s hp: " << u.hp << "\n";
for (int i = 0; i < statNames.size(); i++)
os << statNames[i] << ": " << u.stats[i] << "\n";
os << "Avoid: " << u.avoid << "\n";
os << "hitRate: " << u.hitRate << "\n";
os << "critical avoid: " << u.criticalAvoid << "\n";
os << "critical rate: " << u.criticalRate << "\n";
return os;
}发布于 2019-02-28 12:43:59
当你在编译Skill类时,编译器并不知道类。要使其可用,您必须对Unity类执行"forward声明“。
在Skill.h .h中的标题后面添加以下行
class Unity;发布于 2019-02-28 12:56:43
OP有一个循环依赖: Skill.h .h -> Unit.h .h -> Astra.h -> Skill.h.h。
虽然up包含guardians终止递归(否则会引发编译器错误-类似于“太多嵌套的include”),但它以类型使用(在其中一个头中)结束,因为之前的类型定义丢失(由于有效的头guardian)。
应该防止循环依赖。或者,应该使用转发声明。
请注意,要使用指向类型的指针或引用,不完整的类型就足够了(例如class Unit;)。
因此,头部中的声明可能基于转发声明。因此,至少可以移除循环依赖#include中的一个。
然后,三个C++文件中的每一个都可以对所有三个头文件执行#include操作而不会造成损害。
https://stackoverflow.com/questions/54918322
复制相似问题