简体   繁体   中英

Exception thrown: read access violation [C++]

Hi. I am currently studying programming, so setting aside cleaning up the code for now, I need help in getting the code to run first. Also, I apologize for the huge blocks of code. I don't know if any of this is irrelevant to the question so I posted all of it anyway.

Our current lesson is on classes, and I'm trying to get 2 wizards to duel each other. But before that, I need to assign values to the properties for either wizard:

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;
}

And in my main(), I have this:

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;
}

Assigning values for both wizards and both spells are fine. Then when it goes into the battle loop, this error pops up:

Exception thrown: read access violation.
std::_String_alloc<std::_String_base_types<char,std::allocator<char> > 
>::_Get_data(...) returned nullptr.

And this is where I'm currently stuck. For reference, here's the other 2 functions I have that work in that loop:

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;
    }
}

Any help would be appreciated, and again, I'm sorry for the code dump, I'm sorry the code's bad, I'm sorry this post is so long. I'm at the end of my rope tbh T^T

The problem causing the crash is that you do not assign the spell to the wizard anywhere, so the pointer is default initialised to a null pointer. Within your castSpell function you now dereference this null pointer, trying to access invalid memory (undefined behaviour).

Best is already assigning the spell in the constructor, so you don't ever get into the situation of having invalid values. There are plenty of variants how you could do this:

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
{ }

Variant:

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...
{ }

Now you can define all the parameters already outside and just pass them in (many parameters now, but more flexibility).

OK, now there is so much left to tell: memory leaks (you do not delete the objects created with new), smart pointers (let the deletion be done automatically), move semantics (what's this about the std::move ? – for now, you might just omit it...), encapsulation (your members are public, should be private instead), member functions (so that you can access your private members), inlining or not and hiding implementation details (splitting code into headers and sources), ...

Suppose you'll get to all this later, as you just started learning (but if interested, leave a comment and I'll post a little more...). Just a matter of style yet: Do not compare boolean values against true or false, just use the condition directly:

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!

Similar with return values, if you calculate a boolean value anyway, return it directly:

bool canGoOn(Wizard* wizard)
{
    return wizard->hp > 0 && wizard->mp > 0; // just drop that if/else stuff around
}

This was something I came up with.

Infinite loop when they both run out of mana before they die ;) But you can fix it

#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();
}

Example output:

Name the first wizard: HarryPotter
Name the second wizard: Voldemort
HarryPotter casts FireBall, which does 13 damage to Voldemort
Voldemort casts FireBall, which does 11 damage to HarryPotter
HarryPotter casts FireBall, which does 12 damage to Voldemort
Voldemort casts FireBall, which does 12 damage to HarryPotter
HarryPotter casts FireBall, which does 7 damage to Voldemort
Voldemort casts FireBall, which does 16 damage to HarryPotter
HarryPotter casts FireBall, which does 8 damage to Voldemort
Voldemort casts FireBall, which does 10 damage to HarryPotter
HarryPotter casts FireBall, which does 8 damage to Voldemort
Voldemort casts FireBall, which does 14 damage to HarryPotter
HarryPotter casts FireBall, which does 16 damage to Voldemort
Voldemort casts FireBall, which does 16 damage to HarryPotter
HarryPotter casts FireBall, which does 16 damage to Voldemort
Voldemort casts FireBall, which does 11 damage to HarryPotter
HarryPotter casts FireBall, which does 14 damage to Voldemort
Voldemort casts FireBall, which does 10 damage to HarryPotter
HarryPotter is dead!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM