双向循环链表内存访问错误求助(SFML菜单开发场景)
问题根源
- 浅拷贝引发的双重析构/野指针访问:CDblLinkedList未实现拷贝构造与赋值运算符重载,默认的浅拷贝会让多个链表对象共享节点资源。当CSubMenu被拷贝到链表节点时,若CSubMenu内部也包含CDblLinkedList,两个CSubMenu实例会共享同一份链表数据,析构时会重复释放或访问已失效的指针。
- 不必要的对象拷贝:Insert函数使用值传递参数,导致CSubMenu对象被强制拷贝,放大了浅拷贝的危害。
- CNode构造的冗余拷贝:CNode的构造函数同样使用值传递,会额外拷贝T类型对象。
修复步骤
1. 禁用CDblLinkedList的拷贝操作(若无需拷贝链表)
在CDblLinkedList类的私有成员中添加,禁止默认拷贝构造与赋值:
template<typename T> class CDblLinkedList{ private: CNode<T>*head=nullptr,*current=nullptr; // 禁用拷贝构造与赋值运算符 CDblLinkedList(const CDblLinkedList&) = delete; CDblLinkedList& operator=(const CDblLinkedList&) = delete; public: // ... 原有成员 };
2. 修改Insert与CNode构造为引用传递,避免冗余拷贝
修改CNode的构造函数为const左值引用:
// CNode.h template <typename T> class CNode{ public: // 修改构造函数参数为const引用 CNode(const T& p_data); // ... 原有成员 }; template <typename T> CNode<T>::CNode(const T& p_data):data(p_data){}
修改CDblLinkedList的Insert函数为const引用参数:
// CDblLinkedList.h template<typename T> void CDblLinkedList<T>::Insert(const T& p_data){ CNode<T> * dummy=new CNode<T>(p_data); // ... 原有逻辑不变 }
3. 调整CSubMenu的拷贝策略
如果CSubMenu无需拷贝,同样禁用它的拷贝构造与赋值运算符:
// CSubMenu.h class CSubMenu{ private: // 禁用拷贝构造与赋值 CSubMenu(const CSubMenu&) = delete; CSubMenu& operator=(const CSubMenu&) = delete; public: // ... 原有成员 };
此时若要将CSubMenu插入链表,需使用移动语义转移所有权:
// main.cpp list.Insert(std::move(left));
4. 可选:链表存储智能指针避免对象拷贝
若需要保留原CSubMenu对象,可将链表改为存储std::unique_ptr<T>,完全避免对象拷贝:
// 修改CNode为存储unique_ptr template <typename T> class CNode{ private: CNode<T>*prev=nullptr; std::unique_ptr<T> data; CNode<T>*next=nullptr; public: CNode(std::unique_ptr<T> p_data):data(std::move(p_data)){} // 修改GetData返回T* T* GetData(void){return(data.get());}; // ... 原有成员 }; // 修改Insert函数 template<typename T> void CDblLinkedList<T>::Insert(std::unique_ptr<T> p_data){ CNode<T> * dummy=new CNode<T>(std::move(p_data)); // ... 原有逻辑不变 } // main.cpp中插入方式改为: list.Insert(std::make_unique<CSubMenu>(300,450,"AlgoText",font,&window));
验证修复
完成上述修改后,程序运行时不会再出现浅拷贝导致的野指针访问,析构时的内存释放也会正确执行。
内容的提问来源于stack exchange,提问作者RayanYI
相关产品推荐
相关产品推荐

