【问题标题】:How to prevent a unordered_map of shared pointers from leaking?如何防止共享指针的 unordered_map 泄漏?
【发布时间】:2017-07-25 09:50:09
【问题描述】:

我正在构建一个控制台应用程序,我只使用智能指针。我选择只使用智能指针来了解何时使用哪个智能指针。在这个应用程序中,我尝试使用状态模式在不同状态之间切换。基类是这个类的 TurnState,所有其他状态类都继承。

在游戏控制器中,我定义了当前状态。为了在状态之间切换,我想使用一个 unordered_map,其中枚举作为键,状态类作为值。但是当我在标题中写下std::unordered_map<TurnStateEnum, std::shared_ptr<TurnState>> _turn_states_map; 时,我发现了一些内存泄漏。

为了摆脱那些内存泄漏,我尝试在解构器中破坏它们,如下所示:

GameController::~GameController()
{
    for (std::unordered_map<TurnStateEnum, std::shared_ptr<TurnState>>::iterator iterator{ _turn_states_map.begin() }; iterator != _turn_states_map.end(); iterator++) {
        iterator->second.reset();
        _turn_states_map.erase(iterator);
    }
    _turn_states_map.clear();
}

但这也没有奏效。我能够使用原始指针解决它,但这不是我想要实现的。所以我的问题是,如何以正确的方式删除带有 shared_ptrs 的地图?

我们将不胜感激。


编辑 1 - 最小示例


游戏控制器将用于将 shared_ptr 保持到当前状态并切换到下一个状态。

下面是 GameController 标头:

class GameController
{
public:
    GameController();
    ~GameController();
    void do_action(Socket& client, Player& player, std::string command);
    void set_next_state(TurnStateEnum state);

private:
    std::unordered_map<TurnStateEnum, std::shared_ptr<TurnState>> _turn_states_map;
    std::shared_ptr<TurnState> _turn_state;

    void initialize_turn_states_map();
};

下面是 GameController 源码:

GameController::GameController()
{
    initialize_turn_states_map();
    _turn_state = _turn_states_map.at(TurnStateEnum::SETUP);
}

GameController::~GameController()
{
    for (std::unordered_map<TurnStateEnum, std::shared_ptr<TurnState>>::iterator iterator{ _turn_states_map.begin() }; iterator != _turn_states_map.end(); iterator++) {
        iterator->second.reset();
        _turn_states_map.erase(iterator);
    }
    _turn_states_map.clear();
}

void GameController::do_action(Socket& client, Player& player, std::string command)
{
    _turn_state->do_turn(client, player, command);
}

void GameController::set_next_state(TurnStateEnum state)
{
    _turn_state = _turn_states_map.at(state);
}

void GameController::initialize_turn_states_map()
{
    _turn_states_map.insert(std::make_pair(TurnStateEnum::SETUP, std::make_shared<SetupState>(*this)));
}

TurnState 是基类。此类应包含应用程序的当前逻辑/行为。 TurnState 标头下方:

class GameController;

class TurnState
{
public:
    TurnState(GameController& gameCtrl);
    virtual ~TurnState();

    void next_state(TurnStateEnum stateEnum);
    virtual void do_turn(Socket& client, Player& player, std::string command) = 0;
protected:
    GameController& _gameCtrl;
};

TurnState 源下方:

TurnState::TurnState(GameController& gameCtrl) : _gameCtrl ( gameCtrl )
{
}

TurnState::~TurnState()
{
}

void TurnState::next_state(TurnStateEnum stateEnum)
{
    _gameCtrl.set_next_state(stateEnum);
}

Setup State 除了他的基类之外没有任何其他变量或方法,目前这些方法是空的。


编辑 2 - 最小示例 v2


这可能是一个更好的最小示例。我创建了一个控制台项目并上传到:https://ufile.io/ce79d

【问题讨论】:

  • TurnState 中可能有什么泄漏?
  • @πάνταῥεῖ 我也为此感到害怕。为了测试它,我从 TurnState 制作了一个 shared_ptr 并且它没有泄漏,在它超出范围后它被很好地清理了。
  • 智能指针通常被宣传为修复所有内存泄漏的魔术按钮。这远非真相。确实,正确使用它们意味着永远不必担心deleteing 任何事情,那里的关键词是“正确”。一般来说,您必须了解智能指针的工作原理以及内存的工作原理。自动作用域和动态作用域的区别。例如,循环引用将是泄漏内存的一种方式,尽管您的 gazoo 中有智能指针。您需要花一些时间来充实您的 C++ 书籍,直到您完全理解它们的工作原理、原因和方式。
  • _turn_states_map.erase(iterator) 使iterator 无效。随后的iterator++ 表现出未定义的行为。
  • @user3473161 std::weak_ptr 可能会派上用场寻找解决方案。

标签: c++ c++11 memory-leaks shared-ptr unordered-map


【解决方案1】:

您的程序没有泄漏。您正在正确使用std::shared_ptr。没有要修复的循环引用。尽管析构函数是多余的,但它们是无害的。

您只是没有使用_CrtDumpMemoryLeaks() 对。您在 main 中的本地对象的析构函数运行之前调用它。自然,它会将这些对象分配的内存报告为泄漏。

修复:

int main(int argc, const char * argv[])
{
  (
    GameController gameCtrl = GameController();
    gameCtrl.do_action("test");
  }
  _CrtDumpMemoryLeaks();  

  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-13
    • 2013-08-25
    • 2023-03-15
    • 1970-01-01
    • 2014-08-13
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多