c++ - 使用共享指针双重删除,即使对象仍然有一个 shared_ptr,它的析构函数正在被调用

标签 c++ boost shared-ptr

如果要阅读的代码太多,我深表歉意,如果我可以通过解释简化,请告诉我,如果您想对我的设计/实践发表评论,请随时联系我。

所以我的播放器被删除了两次,我不确定为什么。如果查看调用堆栈,您会发现 GameEventManager 实际上在调用 GameState 析构函数之前调用 Player 析构函数,即使 GameState 是具有指向 Player 的指针的那个。我在想这可能是因为它也首先破坏了 vector ,所以它在 vector 中找到玩家并试图破坏它。我不知道为什么它会尝试破坏 Player,因为仍然有一个 GameState 对象知道的对 Player 的引用。共享指针功能应防止 Player 被破坏。

也许我的做法是错误的,如果可以的话,有人可以在这里为我指出正确的最佳实践方向吗?谢谢

调用堆栈:

enter image description here enter image description here enter image description here 游戏状态.h

#ifndef _level
#define _level
#include <vector>
#include "Player.h"
#include <memory>

using namespace std;

class GameState // A representation of the Level/Game/Scene
{
public:
    GameState();
    virtual ~GameState() {}
    //Keep track of the game's state

    //Maybe get rid of the Level class, and make this a class, and move the Level functionality here?

    static void EndGame(const bool & b) { mbEndGame = b; }

    static const bool & EndGame() { return mbEndGame; }
private:
    void SetPlayer(shared_ptr<Player> sptrPlayer) { msptrPlayer = sptrPlayer; }
    static bool mbEndGame;
    shared_ptr<Player> msptrPlayer;
    vector<shared_ptr<Player>> msptrMob; // Representation of all the NPCs in the game. Eventually make a Mob class but for now just use Player
    // shared_ptr<LevelMap> mMap // Representation of what the game looks like visually
    // Renderer // Should the level have the renderer to create the graphics? Or should this be handled by another "GUI Layer" and interact with this layer as little as possible ?
};

#endif

游戏状态.cpp

#include "stdafx.h"
#include "GameState.h"

bool GameState::mbEndGame(false);

GameState::GameState() : msptrPlayer(NULL)
{
    shared_ptr<Player> sptrPlayer(new Player);
    SetPlayer(sptrPlayer);
}
GameObject.h
#ifndef _game_object
#define _game_object
#include <memory>

// Game Object Classes inherit this so they are registered with GameEventManager.
using namespace std;
class GameObject{
public:

    GameObject();
    virtual ~GameObject() {}
    virtual void Update() = 0;
    virtual void Start() = 0;


};
#endif
GameObject.cpp

#include "stdafx.h"
#include "GameObject.h"
#include "GameEventManager.h"

GameObject::GameObject()
{
    shared_ptr<GameObject> ptr(this);
    GameEventManager::GetGameEventManager()->RegisterGameObject(ptr);
}

播放器.h

#ifndef _player
#define _player

#include "GameObject.h"
//A representation of the Player. Used by Level.
class Player : public GameObject
{
public:
    Player();
    virtual ~Player() {}
private:
    virtual void Update();
    virtual void Start();
    int miMaxHealth;

};
#endif

播放器.cpp

#include "stdafx.h"
#include "Player.h"
#include "GameState.h"

Player::Player() : miMaxHealth(100) {};
void
Player::Start()
{

}

void
Player::Update() // reimplement GameObject::Update(), which is called by the GameEventManager
{
    --miMaxHealth;
    if (miMaxHealth <= 0)
    {
        GameState::EndGame(true);
    }
}

游戏事件管理器.h

#ifndef _game_event_manager
#define _game_event_manager

#include "stdafx.h"
#include <memory>
#include <vector>
#include "GameObject.h"
#include "GameState.h"

using namespace std;

class GameEventManager{ 
    //Object which inherit from GameObject are automatically registered with GameEventManager when they are constructed.
    // GameEventManager creates the level object to represent the game, and then runs Start() on all registered GameObjects
    // and then continually runs Update() on all registered game objects until the GameState is set to EndGame.
public:
    virtual ~GameEventManager(){} // This gets called at the end of the program (I guess whne static variables are destroyed), and crashes during vector<shared pointer <GameObject>> destruction, probably because
                                  // Player already destroyed it. So... not sure what to do. If I make it non-static  

    void StartGameEvents(); 
    const static shared_ptr<GameEventManager>& GetGameEventManager();
    const shared_ptr<GameState>& GetLevel();
    void RegisterGameObject(shared_ptr<GameObject> sptrGameObject);

    const shared_ptr<vector<shared_ptr<GameObject>>>& GetRegisteredGameVector() const { return mvecRegisteredGameVector; }

private:
    GameEventManager(); //singleton
    void AddGameObject(shared_ptr<GameObject>);
    shared_ptr<GameState> mLevel;
    shared_ptr<vector<shared_ptr<GameObject>>> mvecRegisteredGameVector; //Reference because shared pointer will double delete otherwise. ~Level() still deletes it but this way I guess it doesn't try to delete again? but...
                                                                        //Now I'm trying it as a shared_ptr, but it's not working. ~Level() still deletes it even though there is a shared pointer to a vector pointing to the Player. Why is ~Level() doing this?

    static shared_ptr<GameEventManager> msptrGameEventManager;
};
#endif

游戏事件管理器.cpp

#include "stdafx.h"
#include "GameEventManager.h"
#include "GameState.h"

shared_ptr<GameEventManager> GameEventManager::msptrGameEventManager(new GameEventManager);

void
GameEventManager::StartGameEvents()
{
    //run once
    int size = GetRegisteredGameVector()->size();
    vector<shared_ptr<GameObject>> & vecsptrRegisteredGameVector = (*GetRegisteredGameVector());
    for (int i = 0; i < GetRegisteredGameVector()->size(); ++i)
    {
        vecsptrRegisteredGameVector[i]->Start(); //nothing for now 
    }
    //keep running
    while (GetLevel()->EndGame() != true)
    {
        for (int i = 0; i < GetRegisteredGameVector()->size(); i++)
        {
            (*GetRegisteredGameVector())[i]->Update(); //Player's life goes from 100 to zero, see Player::Update

        }
    }
    return; 
    // GameState destructor is called and destroys player for some reason, even though it's still being referenced by the GameEventManager's vector.
}

GameEventManager::GameEventManager() : mvecRegisteredGameVector(new vector<shared_ptr<GameObject>>) , mLevel(NULL) //Instantiating the level before the GameEventManager is fully instantiated causes an infinite recursion.
{
    return;
}

const shared_ptr<GameEventManager>& 
GameEventManager::GetGameEventManager()
{
    if (!msptrGameEventManager)
    {
        msptrGameEventManager.reset(new GameEventManager);
    }
    return msptrGameEventManager;
}

const shared_ptr<GameState>&
GameEventManager::GetLevel()
{
    if (!mLevel)
    {
        mLevel.reset(new GameState);
    }
    return mLevel;
}
void 
GameEventManager::RegisterGameObject(shared_ptr<GameObject> sptrGameObject)
{
    GetGameEventManager()->AddGameObject(sptrGameObject);
}
void
GameEventManager::AddGameObject(shared_ptr<GameObject> sptrGameObject)
{
    GetRegisteredGameVector()->push_back(sptrGameObject);
}

最佳答案

GameObject::GameObject()
{
    shared_ptr<GameObject> ptr(this);
    GameEventManager::GetGameEventManager()->RegisterGameObject(ptr);
}

ptr在此功能中不与任何其他人共享所有权shared_ptr独立构建,就像在 shared_ptr<Player> sptrPlayer(new Player); 声明的那样.创建两个 shared_ptr来自原始指针,而不是复制第一个指针,通常会导致双重删除。

相反,您可以这样做:

class GameObject :
    public std::enable_shared_from_this<GameObject>
{
protected:
    GameObject(); // creates the original shared_ptr
    virtual ~GameObject();
};

class Player : public GameObject
{
public:
    static std::shared_ptr<Player> create();
private:
    Player() : GameObject() {}
    virtual ~Player() {}
};

std::shared_ptr<Player> Player::create() {
    return dynamic_pointer_cast<Player>((new Player)->shared_from_this());
}

关于c++ - 使用共享指针双重删除,即使对象仍然有一个 shared_ptr,它的析构函数正在被调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24977582/

相关文章:

c++ - 结构的指针以及如何访问元素

c++ - 为什么 std::istreambuf_iterator 无法进行 boost SinglePass Iterator 概念检查?

c++ - 为什么 boost this_thread::interrupt 可以在没有 try-catch 的情况下工作?

c++ - 在 shared_ptr 的 unordered_set 中找到一个值

c++ - 即使在 std::shared_ptr 拥有之后,shared_from_this 还是空的 _M_weak_this

c++ - 使用 RAW 指针对 boost::shared_ptr 变量进行条件初始化

c++ - 打开原始磁盘并获取大小 OS X

c++ - 在 emplace_back() 中初始化内部结构

c++ - 使用递归宏编写函数的参数

c++ - 什么是 jamfile?