C++ 引发异常:读取访问冲突[C+;+;]

C++ 引发异常:读取访问冲突[C+;+;],c++,C++,嗨。我目前正在学习编程,所以暂且不考虑清理代码,我需要帮助让代码首先运行。另外,我为大量的代码块道歉。我不知道这些是否与问题无关,所以我还是把它们全部贴了出来 我们现在的课程是课堂,我想让两个巫师互相决斗。但在此之前,我需要为以下任一向导的属性赋值: class Spell { public: string name; unsigned int cost; unsigned int dmg; }; class Wizard { public: string na

嗨。我目前正在学习编程,所以暂且不考虑清理代码,我需要帮助让代码首先运行。另外,我为大量的代码块道歉。我不知道这些是否与问题无关,所以我还是把它们全部贴了出来

我们现在的课程是课堂,我想让两个巫师互相决斗。但在此之前,我需要为以下任一向导的属性赋值:

class Spell
{
public:
    string name;
    unsigned int cost;
    unsigned int dmg;
};

class Wizard
{
public:
    string name;
    unsigned int hp;
    unsigned int mp;
    Spell* spell;
};


void assignWizardValues(Wizard* wizard, Spell* spell)
{
    wizard->hp = rand() % 25 + 76;
    wizard->mp = rand() % 25 + 76;
    spell->name = "Fireball";
    spell->cost = rand() % 10 + 6;
    spell->dmg = rand() % 10 + 6;
}
在我的main()中,我有:

int main()
{
    Wizard* wiz1 = new Wizard();
    Wizard* wiz2 = new Wizard();
    Spell* fireball1 = new Spell();
    Spell* fireball2 = new Spell();

    //Assign Property Values
    srand(time(NULL)); 

    cout << "Who is the first wizard?  ";
    cin >> wiz1->name;
    assignWizardValues(wiz1, fireball1);

    cout << "Who is the second Wizard?  ";
    cin >> wiz2->name;
    assignWizardValues(wiz2, fireball2);

    //Battle START!!

    while (canGoOn(wiz1) == true && canGoOn(wiz2) == true)
    {
        castSpell(wiz1, wiz2);
        castSpell(wiz2, wiz1);
    }

    system("pause");
    return 0;
}
intmain()
{
向导*wiz1=新建向导();
Wizard*wiz2=新建向导();
拼写*fireball1=新拼写();
拼写*fireball2=新拼写();
//指定属性值
srand(时间(空));
cout>wiz1->name;
赋值(wiz1,火球1);
cout>wiz2->name;
赋值(wiz2,fireball2);
//战斗开始!!
while(canGoOn(wiz1)==true&&canGoOn(wiz2)==true)
{
CastSpill(wiz1,wiz2);
CastSpill(wiz2,wiz1);
}
系统(“暂停”);
返回0;
}
为两个向导和两个法术赋值是可以的。然后,当它进入战斗循环时,会弹出以下错误:

Exception thrown: read access violation.
std::_String_alloc<std::_String_base_types<char,std::allocator<char> > 
>::_Get_data(...) returned nullptr.
引发异常:读取访问冲突。 std::_String_alloc::_Get_data(…)返回nullptr。 这就是我目前所处的困境。 作为参考,以下是我在该循环中使用的其他两个函数:

void castSpell(Wizard* caster, Wizard* enemy)
{
    cout << caster->spell->name << endl;
    caster->mp -= caster->spell->cost;
    enemy->hp -= caster->spell->dmg;
    cout << caster->hp << endl << caster->mp << endl << endl;
    cout << enemy->hp << endl << enemy->mp << endl << endl;
    cout << endl << endl;
}

bool canGoOn(Wizard* wizard)
{
    if (wizard->hp > 0 && wizard->mp > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}
无效施法(法师*施法者,法师*敌人)
{
不能拼写->名称mp-=施法者->拼写->成本;
敌人->生命-=施法者->咒语->魔法咒语;

cout hp导致崩溃的问题是,您没有将拼写分配给向导的任何位置,因此指针默认初始化为空指针。在
castSpell
函数中,您现在取消引用此空指针,试图访问无效内存(未定义的行为)

最好的方法是已经在构造函数中分配了拼写,这样你就不会陷入无效值的情况。有很多方法可以做到这一点:

Wizard(std::string name)
    : name(std::move(name)), // std::move: avoids unnecessary copying of data...
      hp(rand() % 25 + 76),
      mp(rand() % 25 + 76),
      spell(new Spell("Fireball"))
      // assuming Spell has a constructor similar to this one
{ }
变体:

Wizard
(
    std::string name, unsigned int hp, unsigned int mp,
    std::string spellName, unsigned int spellCost, unsigned int spellDamage
)
    : name(std::move(name)),
      hp(hp), mp(mp),
      spell(new Spell(std::move(spellName), spellCost, spellDamage))
      // same assumption...
{ }
现在,您可以定义所有已经在外部的参数,只需将它们传入(现在有许多参数,但更具灵活性)

好的,现在还有很多事情要说:内存泄漏(你不删除用new创建的对象)、智能指针(让删除自动完成)、移动语义(关于
std::move
?——现在,你可能忽略它……),封装(你的成员是公共的,应该是私有的),成员函数(以便您可以访问私有成员),是否内联和隐藏实现细节(将代码拆分为头和源代码)

假设您稍后将了解所有这些内容,因为您刚刚开始学习(但如果有兴趣,请留下评论,我将发布更多…)。只是风格问题:不要将布尔值与true或false进行比较,直接使用条件:

while (canGoOn(wiz1) == true && canGoOn(wiz2) == false) // bad style
//                                               ^^^^^
// changed just for demonstration purposes!

while (canGoOn(wiz1) && !canGoOn(wiz2))  // the way to go.
//                      ^ to check if condition is NOT met...
//                        again for demonstration only, not in your REAL code!
bool canGoOn(Wizard* wizard)
{
    return wizard->hp > 0 && wizard->mp > 0; // just drop that if/else stuff around
}
与返回值类似,如果仍然计算布尔值,请直接返回:

while (canGoOn(wiz1) == true && canGoOn(wiz2) == false) // bad style
//                                               ^^^^^
// changed just for demonstration purposes!

while (canGoOn(wiz1) && !canGoOn(wiz2))  // the way to go.
//                      ^ to check if condition is NOT met...
//                        again for demonstration only, not in your REAL code!
bool canGoOn(Wizard* wizard)
{
    return wizard->hp > 0 && wizard->mp > 0; // just drop that if/else stuff around
}

这是我想到的

无限循环,当他们都在死前耗尽法力;)但你可以修复它

#include<random>
std::random_device rd;  //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()

int ThrowDie(int dieSize)
{
    std::uniform_int_distribution<> dis(1, dieSize);
    return dis(gen);
}

#include <string>
#include <utility>
class Spell {
    const int cost_base;
    const int cost_die;
    const int damage_base;
    const int damage_die;
public:
    const std::string name;

    Spell(const std::string& name, int cost_base, int cost_die,
        int damage_base, int damage_die)
        : name(name), cost_base(cost_base), cost_die(cost_die)
        , damage_base(damage_base), damage_die(damage_die) {}
    virtual ~Spell() = default;
    // returns <cost, damage>
    std::pair<int, int> Cast();
};

std::pair<int, int> Spell::Cast()
{
    return std::make_pair(
        cost_base + ThrowDie(cost_die),
        damage_base + ThrowDie(damage_die)
    );
}

class Fireball : public Spell {
public:
    Fireball() : Spell("FireBall", 6, 10, 6, 10) {}
    using Spell::Cast;
};

class Wizard
{
public:
    Wizard(const std::string& name);
    void cast(Spell spell, Wizard& opponent);
    bool IsAlive();
private:
    const std::string name;
    int healthPoints;
    int manaPoints;
};

Wizard::Wizard(const std::string& name)
    : name(name)
{
    healthPoints = 76 + ThrowDie(25);
    manaPoints = 76 + ThrowDie(25);
}

#include <iostream>
void Wizard::cast(Spell spell, Wizard& opponent)
{
    auto reqEff = spell.Cast();
    if (reqEff.first > manaPoints)
    {
        std::cout << name << " does not have enough mana points to cast " << spell.name << "\n";
    }
    else
    {
        manaPoints -= reqEff.first;
        opponent.healthPoints -= reqEff.second;
        std::cout << name << " casts " << spell.name << ", which does "
            << reqEff.second << " damage to " << opponent.name <<"\n";
    }
}

bool Wizard::IsAlive()
{
    if (healthPoints > 0)
    {
        //std::cout << name << " is still alive!\n"; \\ a lot of text...
        return true;
    }
    std::cout << name << " is dead!" << std::endl;
    return false;
}

#include <iostream>
int main()
{
    std::string name;
    std::cout << "Name the first wizard: ";
    std::cin >> name;
    Wizard wiz1(name);
    std::cout << "Name the second wizard: ";
    std::cin >> name;
    Wizard wiz2(name);

    // Battle start
    while (wiz1.IsAlive() && wiz2.IsAlive()) {
        wiz1.cast(Fireball(), wiz2);
        wiz2.cast(Fireball(), wiz1);
    }

    std::cin.ignore();
}

您将
向导
类的
拼写成员分配到哪里?非主题:不要比较
if(condition==true)
/
if(condition==false)
;只需使用
if(condition)
/
if(!condition)
相反……请注意,您的成员变量是未签名的。当hps小于伤害时会发生什么?此外,向导和法术在免费存储区中使用new in main分配(请研究构造函数和RAII),但从未删除。为什么要在堆上创建对象?为什么不使用
向导wiz1;
。并通过引用传递到函数?谢谢!所有答案都很有帮助,但这一个特别让我看到了我在需要做的事情上的错误(还有一些我没有注意到的错误).我在编码方面相对较新,所以这是非常有用的,谢谢(再次)!