You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MFC对话框中对象池与容器结合释放时崩溃问题求助

问题:对象池释放时崩溃排查与修复

在MFC CDialog项目中实现对象池并应用到set、vector、map容器,退出对话框时调用SAFEDELETE(_ContainerPool)必定崩溃,需实现正常释放。


数据存储结构代码

#include <set>
#include <map>
#include <string>
#include <memory>
#include <vector>
using namespace std;
#define SAFEDELETE(p) {if (p) { delete p; p = nullptr;}}
class CName
{
public:
    bool operator < (const CName& other) const
    {
        if (this->_server.compare(other._server) != 0)
        {
            return this->_server < other._server;
        }
        if (this->_source.compare(other._source) != 0)
        {
            return this->_source < other._source;
        }
        return false;
    }
public:
    string _server;
    string _source;
};

class Attributes
{
    public:
        Attributes() {}
        ~Attributes()
        {
            _enabled = false;
            _lTimeStamp = 0;
        }
        bool _enabled = false;
        long _lTimeStamp = 0;
};
using AttributesPtr = shared_ptr<Attributes>;
class TimeStampCompare
{
public:
    bool operator()(const AttributesPtr attr1, const AttributesPtr attr2) const
    {
        if (attr1 == nullptr || attr2 == nullptr)
        {
            return false;
        }
        return attr1->_lTimeStamp > attr2->_lTimeStamp;
    }
};
class Container;
using ContainerPtr = shared_ptr<Container>;
class Container
{
public:
    Container() {}
    ~Container()
    {
        Clear();
    }
    void Clear()
    {
        _name = "";
        _path = "";
        for (auto& iter : _cache)
        {
            iter.second.clear();
        }
        _cache.clear();
        _containers.clear();
    }
public:
        string _name;
        string _path;
        vector<ContainerPtr> _containers;
        map<CName, set<AttributesPtr, TimeStampCompare>> _cache;
};

对象池代码

template <typename T>
class ObjectPool
{
public:
    ObjectPool(std::size_t size)
    {
        for (std::size_t i = 0; i < size; i++)
        {
            T* obj = new T();
            m_objs.push_back(obj);
        }
    }
    virtual ~ObjectPool()
    {
        for (auto iter = m_objs.begin(); iter != m_objs.end(); iter ++)
        {
            delete (*iter);
        }
        m_objs.clear();
    }
    template <typename... E>
    std::shared_ptr<T> getObject()
    {
        T* obj = iGetObject();
        if (obj != nullptr)
        {
            return std::shared_ptr<T>(obj, [this](T* ptr)->void
            {
                destroy1(ptr);
                std::unique_lock<std::mutex> lk(m_lock);
                m_objs.push_back(ptr);
            });
        }
        return nullptr;
    }   
private:
    std::deque<T*> m_objs;
    mutable std::mutex m_lock;
    T* iGetObject()
    {
        std::unique_lock<std::mutex> lk(m_lock);
        if (!m_objs.empty())
        {
            T* obj = m_objs.front();
            m_objs.pop_front();
            return obj;
        }
        return nullptr;
    }
    void destroy1(T* obj)
    {
        obj->~T();
    }
};

对话框中成员变量的创建与析构代码

//CMyDialog.h
ContainerPtr _root;
ObjectPool<Container>* _ContainerPool = nullptr;
ObjectPool<Attributes>* _AttributePool = nullptr;

//CMhDialog.cpp
//construction
_ContainerPool = new ObjectPool<Container>(1);
_AttributePool = new ObjectPool<Attributes>(1);
_root = _ContainerPool->getObject();

//destruction
_root = nullptr;
SAFEDELETE(_ContainerPool);
SAFEDELETE(_AttributePool);

崩溃原因分析

  1. 双重析构对象:对象池回收shared_ptr时,自定义删除器调用destroy1手动执行了对象的析构函数(obj->~T());而对象池销毁时,又通过delete (*iter)再次调用对象的析构函数。同一个对象的析构函数被执行两次,触发未定义行为导致崩溃。
  2. 悬空指针风险:shared_ptr的删除器捕获了对象池的this指针,若shared_ptr生命周期超过对象池,会访问已释放的内存(当前代码中_root先被置空,此风险未直接触发,但仍需规避)。

修复方案

修改对象池逻辑,避免双重析构

核心思路:对象的析构仅由对象池在销毁时执行一次,回收对象时仅重置其状态而非调用析构函数。

修改后的对象池代码:

template <typename T>
class ObjectPool
{
public:
    ObjectPool(std::size_t size)
    {
        for (std::size_t i = 0; i < size; i++)
        {
            T* obj = new T();
            m_objs.push_back(obj);
        }
    }
    ~ObjectPool()
    {
        std::unique_lock<std::mutex> lk(m_lock);
        for (auto obj : m_objs)
        {
            delete obj; // 唯一一次调用对象析构
        }
        m_objs.clear();
    }
    std::shared_ptr<T> getObject()
    {
        T* obj = iGetObject();
        if (obj != nullptr)
        {
            return std::shared_ptr<T>(obj, [this](T* ptr)->void
            {
                // 根据对象类型重置状态,以便复用
                if constexpr (std::is_same_v<T, Container>) {
                    ptr->Clear();
                } else if constexpr (std::is_same_v<T, Attributes>) {
                    ptr->_enabled = false;
                    ptr->_lTimeStamp = 0;
                }
                std::unique_lock<std::mutex> lk(m_lock);
                m_objs.push_back(ptr);
            });
        }
        return nullptr;
    }   
private:
    std::deque<T*> m_objs;
    mutable std::mutex m_lock;
    T* iGetObject()
    {
        std::unique_lock<std::mutex> lk(m_lock);
        if (!m_objs.empty())
        {
            T* obj = m_objs.front();
            m_objs.pop_front();
            return obj;
        }
        return nullptr;
    }
};

额外优化建议

  • 保留_root = nullptr在SAFEDELETE(_ContainerPool)之前执行,确保所有从池取出的shared_ptr都已完成回收。
  • 若要支持更多类型的对象复用,可给对象类定义统一的重置接口(比如Reset()),避免在删除器中写类型判断。

内容的提问来源于stack exchange,提问作者Shirley Shirley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:58:14