引发异常:读取访问冲突。 std :: shared_ptr <Weapon> :: operator -> <Weapon, 0> (...) returned nullptr., happened

Exception raised: read access violation. std :: shared_ptr <Weapon> :: operator -> <Weapon, 0> (...) returned nullptr., happened

问题

程序启动和调试过程中出现异常。使用 get ????? 获取数据时出现异常() 方法;武器class。异常看起来像这样: 出现异常:侵犯阅读权限。 std :: shared_ptr :: operator -> (...) 返回 nullptr。 Hero.h中的代码注释掉了问题片段,方法调用。

Main.cpp

#include "Hero.h"
#include <iostream>
#include <windows.h>
#include <cstdlib>
#include <time.h>

Hero* GetSomeHero()
{
    switch (1 + rand() % 5)
    {
    case 1: return new Human(100);
    case 2: return new King(300);
    case 3: return new Queen(150);
    case 4: return new Knight(200);
    case 5: return new Troll(250);
    }
    return new Human(100);
}

int main()
{
    srand((unsigned int)time(0));
    std::shared_ptr<Hero> hero(GetSomeHero());
    std::shared_ptr<Hero> enemy(GetSomeHero());


    while (true)
    {
        hero->attackOutput(*enemy);
        enemy->attackOutput(*hero);

        if (hero->isDead())
        {
            std::shared_ptr<Hero> temp(GetSomeHero());
            hero.swap(temp);
        }
        if (enemy->isDead())
        {
            std::shared_ptr<Hero> temp(GetSomeHero());
            enemy.swap(temp);
        }

        std::cout << *hero << std::endl << *enemy << std::endl;
        Sleep(200);
        system("cls");
    }

    system("pause");
    return 0;
}

Weapon.h

#pragma once
#include <string>

class Weapon
{
protected:
    std::string name;
    float damage;
    int wear;
public:
    Weapon(std::string name, float damage, int wear)
    {
        this->name = name;
        this->damage = damage;
        this->wear = wear;
    }
    virtual void setWear(int wear) = 0;
    virtual std::string getName() = 0;
    virtual float getDamage() = 0;
    virtual int getWear() = 0;
};

class Fist : public Weapon // KULAK
{
protected:
public:
    Fist() : Weapon("Fist", 1, 3) {}
    virtual void setWear(int wear)
    {
        this->wear = wear;
    }
    virtual std::string getName()
    {
        return this->name;
    }
    virtual float getDamage()
    {
        return this->damage;
    }
    virtual int getWear()
    {
        return this->wear;
    }
};

class Knife : public Weapon // NOZH
{
protected:
public:
    Knife() : Weapon("Knife", 5, 5) {}
    virtual void setWear(int wear)
    {
        this->wear = wear;
    }
    virtual std::string getName()
    {
        return this->name;
    }
    virtual float getDamage()
    {
        return this->damage;
    }
    virtual int getWear()
    {
        return this->wear;
    }
};

class Bow : public Weapon // LUCK
{
protected:
public:
    Bow() : Weapon("Bow", 15, 10) {}
    virtual void setWear(int wear)
    {
        this->wear = wear;
    }
    virtual std::string getName()
    {
        return this->name;
    }
    virtual float getDamage()
    {
        return this->damage;
    }
    virtual int getWear()
    {
        return this->wear;
    }
};

class Ax : public Weapon // TOPOR
{
protected:
public:
    Ax() : Weapon("Ax", 30, 5) {}
    virtual void setWear(int wear)
    {
        this->wear = wear;
    }
    virtual std::string getName()
    {
        return this->name;
    }
    virtual float getDamage() 
    {
        return this->damage;
    }
    virtual int getWear()
    {
        return this->wear;
    }
};

class Sword : public Weapon // MECH
{
protected:
public:
    Sword() : Weapon("Sword", 25, 8) {}
    virtual void setWear(int wear)
    {
        this->wear = wear;
    }
    virtual std::string getName()
    {
        return this->name;
    }
    virtual float getDamage()
    {
        return this->damage;
    }
    virtual int getWear()
    {
        return this->wear;
    }
};

Hero.h

#include "Weapon.h"
#include <iostream>
#include <string>
#include <cstdlib>

class Hero
{
protected:
    std::shared_ptr<Weapon> weapon;
    std::string name;
    float health;
    int pressure;
    int beauty;
    int skill;
    int horror;
public:
    Hero(std::string name, float health, int pressure, int beauty, int skill, int horror)
    {
        this->name = name;
        this->health = health;
        this->pressure = 1 + rand() % pressure;;
        this->beauty = 1 + rand() % beauty;
        this->skill = 1 + rand() % skill;
        this->horror = 1 + rand() % horror;
    }
    friend std::ostream& operator<<(std::ostream& os, const Hero& obj)
    {
        os << std::endl
            << obj.name << std::endl
            << "Health: " << obj.health << std::endl
            /*<< "Weapon: " << obj.weapon->getName() << "[" << obj.weapon->getDamage() << "]\n"*/
            << "Specifications:\n"
            << "Pressure[" << obj.pressure << "], Beauty[" << obj.beauty << "], Skill[" << obj.skill << "], Horror[" << obj.pressure << "]\n";
        return os;
    }
    bool isDead()
    {
        if (this->health > 0) return false;
        else return true;
    }
    virtual void weaponСhange() = 0;
    virtual void attackOutput(Hero& enemy) = 0;
    virtual void attackInput(int damage) = 0;
};

class Human : public Hero
{
protected:
public:
    Human(float health) : Hero("Human", health, 1, 1, 1, 1) {}
    virtual void weaponСhange()
    {
        weapon = std::shared_ptr<Weapon>(new Fist);
    }
    virtual void attackOutput(Hero& enemy)
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        enemy.attackInput(1 /*+ (int)this->weapon->getDamage()*/);
        /*this->weapon->setWear(this->weapon->getWear() - 1);*/
    }
    virtual void attackInput(int damage)
    {
        this->health -= damage;
    }
};

class King : public Hero
{
protected:
public:
    King(float health) : Hero("King", health, 10, 15, 4, 3) {}
    virtual void weaponСhange()
    {
        switch (1 + rand() % 3)
        {
        case 1: weapon = std::shared_ptr<Weapon>(new Fist);
        case 2: weapon = std::shared_ptr<Weapon>(new Knife);
        case 3: weapon = std::shared_ptr<Weapon>(new Ax);
        case 4: weapon = std::shared_ptr<Weapon>(new Sword);
        }
    }
    virtual void attackOutput(Hero& enemy)
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        enemy.attackInput(1 + this->pressure + this->skill /*+ (int)this->weapon->getDamage()*/);
        /*this->weapon->setWear(this->weapon->getWear() - 1);*/
    }
    virtual void attackInput(int damage)
    {
        this->health -= damage;
    }
};

class Queen : public Hero
{
protected:
public:
    Queen(float health) : Hero("Queen", health, 2, 15, 10, 1) {}
    virtual void weaponСhange()
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        switch (1 + rand() % 2)
        {
        case 1: weapon = std::shared_ptr<Weapon>(new Fist);
        case 2: weapon = std::shared_ptr<Weapon>(new Knife);
        case 3: weapon = std::shared_ptr<Weapon>(new Bow);
        }
    }
    virtual void attackOutput(Hero& enemy)
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        enemy.attackInput(1 + this->beauty + this->skill /*+ (int)this->weapon->getDamage()*/);
        /*this->weapon->setWear(this->weapon->getWear() - 1);*/
    }
    virtual void attackInput(int damage)
    {
        this->health -= damage;
    }
};

class Knight : public Hero
{
protected:
public:
    Knight(float health) : Hero("Knight", health, 15, 6, 20, 1) {}
    virtual void weaponСhange()
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        switch (1 + rand() % 2)
        {
        case 1: weapon = std::shared_ptr<Weapon>(new Fist);
        case 2: weapon = std::shared_ptr<Weapon>(new Ax);
        case 3: weapon = std::shared_ptr<Weapon>(new Sword);
        }
    }
    virtual void attackOutput(Hero& enemy)
    {
        if (weapon->getWear() <= 0) this->weaponСhange();
        enemy.attackInput(1 + this->skill + this->pressure /*+ (int)this->weapon->getDamage()*/);
        /*this->weapon->setWear(this->weapon->getWear() - 1);*/
    }
    virtual void attackInput(int damage)
    {
        this->health -= damage;
    }
};

class Troll : public Hero
{
protected:
public:
    Troll(float health) : Hero("Troll", health, 20, 1, 1, 10) {}
    virtual void weaponСhange()
    {
        /*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
        switch (1 + rand() % 1)
        {
        case 1: weapon = std::shared_ptr<Weapon>(new Fist);
        case 2: weapon = std::shared_ptr<Weapon>(new Ax);
        }
    }
    virtual void attackOutput(Hero& enemy)
    {
        if (weapon->getWear() <= 0) this->weaponСhange();
        enemy.attackInput(1 + this->horror + this->pressure /*+ (int)this->weapon->getDamage()*/);
        /*this->weapon->setWear(this->weapon->getWear() - 1);*/
    }
    virtual void attackInput(int damage)
    {
        this->health -= damage;
    }
};

导致崩溃的问题很可能是您在 attackOutput() 中为 KnightTroll 调用了 weapon->getWear()。你在为他们分配武器之前执行此操作。

另一个问题:您还没有将基 class 设为析构函数 virtual。这意味着智能指针只会在销毁对象时销毁对象的基 class 部分。

第三个问题:你的很多switch都会给变量赋值,但不会break所以值会被一遍又一遍的覆盖,最后会被赋值为上次的值case。在这种情况下,您将获得一个随机数 [1, 3](而不是 [1, 4]),并且每次都会获得一个 Sword

        switch (1 + rand() % 3)
        {
        case 1: weapon = std::shared_ptr<Weapon>(new Fist);  // missing break;
        case 2: weapon = std::shared_ptr<Weapon>(new Knife); // missing break;
        case 3: weapon = std::shared_ptr<Weapon>(new Ax);    // missing break;
        case 4: weapon = std::shared_ptr<Weapon>(new Sword); // missing break;
        }

旁注:

  • 首选weapon = std::make_shared<Fist>();等而不是上面的

  • 首选 std::unique_ptr 而不是 std::shared_ptr 除非你真的需要引用计数指针。

  • 为伤害和健康等选择一种类型。您现在混合使用 floatint

  • 当你重写子class中的成员函数时,你可以跳过virtual说明符(没关系),而是添加override说明符。如果该函数实际上没有覆盖虚函数,这将使编译器报错。

  • 在逻辑表达式中为

    bool isDead() {
        if (this->health > 0) return false;
        else return true;
    }
    

    您在 if(...) 中已经有一个布尔结果,所以更喜欢直接 return 它。在这种情况下,您可以简单地改为这样做:

    bool isDead() {
        return this->health <= 0;
    }
    
  • 使用 <random> 随机数生成器和支持函数以获得更好的随机化。由于您在 1 和某个最大值(含)之间生成了很多数字,因此您可以为它创建一个支持函数。示例:

    #include <random>
    
    int my_rand(int max) {
        // the generator will only be seeded once since it's static
        static std::mt19937 generator(std::random_device{}());
    
        std::uniform_int_distribution<int> dist(1, max); // range: [1, max]
        return dist(generator);
    }
    
  • 如果您有一些虚函数的默认实现,您可以在基础 class 中实现它以节省很多 copy/pasting。只有在你想要一些特别的东西的情况下才覆盖它。一些 Weapons 的示例:

    class Weapon {
    private:
        // Direct access to member variables is usually not a good idea. Try to keep them
        // private.
        std::string name;
        float damage;
        int wear;
    protected:
        // The constructor can be protected, only subclasses will be able to use it.
        Weapon(std::string Name, float Damage, int Wear) : // use the initializer list
            name(Name), damage(Damage), wear(Wear)
        {}
    
        // virtual destructor
        virtual ~Weapon() = default;
    
    public:
        virtual void setWear(int Wear) { wear = Wear; }
        virtual void applyWear() { --wear; }
        virtual const std::string& getName() { return name; }
        virtual float getDamage() const { return damage; }
        virtual int getWear() { return wear; }
    };
    
    class Fist : public Weapon // KULAK
    {
    public:
        Fist() : Weapon("Fist", 1, 3) {}
    };
    
    class Knife : public Weapon // NOZH
    {
    public:
        Knife() : Weapon("Knife", 5, 5) {}
    };
    ...