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);
崩溃原因分析
- 双重析构对象:对象池回收
shared_ptr时,自定义删除器调用destroy1手动执行了对象的析构函数(obj->~T());而对象池销毁时,又通过delete (*iter)再次调用对象的析构函数。同一个对象的析构函数被执行两次,触发未定义行为导致崩溃。 - 悬空指针风险:
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
相关产品推荐
相关产品推荐

