释放含模板结构体指针的队列节点时触发堆损坏错误求助
我第一次用C++模板实现一个能存储任意类的简易Queue数据结构,写了TestClass测试,但释放队列节点时总是触发堆损坏错误。怀疑是TestClass大小远大于QueueNode导致的,但不确定具体原因。试过释放节点本身、置空data等操作都无法解决问题。
相关代码
QueueTest.h
#include "../../src/Core/Logging/Logging.h" #include <iostream> #include <string> class TestClass { public: int num; std::string str; TestClass(int inNum, std::string inStr); }; int TestQueueMain(Logger Logs); int TestEdgeCase1();
QueueTest.cpp
#include "QueueTest.h" #include "../../src/Core/DataStructures/Queue.h" TestClass::TestClass(int inNum, std::string inStr) : num(inNum), str(inStr) {} std::ostream& operator<< (std::ostream &out, TestClass const& data) { out << "Num: " << data.num << " Str: " << data.str; return out; } int TestQueueMain(Logger Logs) { Logs.createLog(Info, "Queue tests started", std::source_location::current()); int failures = 0; failures += TestEdgeCase1(); Logs.createLog(Info, "Queue tests finished with " + std::to_string(failures) + " error(s)", std::source_location::current()); return failures; } int TestEdgeCase1() { Queue q = Queue<TestClass>(); TestClass t1 = TestClass(1, "a"); std::cout << sizeof(t1) << std::endl; // 输出48 q.Enqueue(&t1); std::cout << "test" << std::endl; TestClass temp = q.Dequeue(); // 错误发生在这里 std::cout << temp; return 0; }
Queue.h
#include <iostream> template <class T> struct QueueNode { T* data; // 存储任意类的指针 struct QueueNode* next; }; template <class T> class Queue { public: Queue() : Start(nullptr), End(nullptr), Size(0) {} ~Queue() { Clean(); } template<class T> inline void Enqueue(T* Obj) { QueueNode<T>* node = (QueueNode<T>*)malloc(sizeof(QueueNode<T>*)); std::cout << "Inserting obj: " << *Obj << std::endl; node->data = Obj; node->next = nullptr; if (Start == nullptr) { Start = node; End = node; } else { End->next = node; End = node; } } inline T Dequeue() { if (Start == nullptr) return *Start->data; // 返回空指针(逻辑错误) else if (Start == End) { std::cout << "START SIZE: " << sizeof(Start) << std::endl; // 输出8 std::cout << "START SIZE2: " << sizeof(Start->data) << std::endl; //输出8 T val = *Start->data; End = nullptr; Start->data = nullptr; free(Start); return val; } QueueNode<T>* temp = Start; T val = *Start->data; Start = Start->next; free(temp); return val; } inline void Clean() { struct QueueNode<T>* temp; while (Start != nullptr) { temp = Start; Start = Start->next; free(temp); } free(Start); std::cout << "Cleaning queue" << std::endl; } private: QueueNode<T>* Start; QueueNode<T>* End; int Size; };
问题根源与修复方案
1. 内存分配大小错误(核心问题)
在Enqueue方法中,你用malloc(sizeof(QueueNode<T>*))分配内存,这只分配了一个指针的大小(64位系统为8字节),但QueueNode<T>包含两个指针成员(T*和QueueNode<T>*),实际需要16字节内存。分配的内存不足,写入node->next时会直接越界,触发堆损坏。
修复:将内存分配的大小改为sizeof(QueueNode<T>):
QueueNode<T>* node = (QueueNode<T>*)malloc(sizeof(QueueNode<T>));
2. C++中malloc/free的不规范使用
虽然QueueNode是POD结构,用malloc没问题,但C++中更推荐用new/delete管理对象,避免后续扩展非POD类型时出问题:
// Enqueue替换malloc QueueNode<T>* node = new QueueNode<T>(); node->data = Obj; node->next = nullptr; // Dequeue和Clean中替换free delete temp;
3. 空队列访问崩溃问题
当队列为空时,Dequeue中直接返回*Start->data会访问空指针的成员,导致崩溃。需要先判断空队列:
inline T Dequeue() { if (Start == nullptr) { // 可选:抛出异常或返回默认构造对象 throw std::runtime_error("Queue is empty"); // 或 return T(); } // 后续逻辑... }
4. Clean方法重复释放问题
循环结束后Start已经是nullptr,再调用free(Start)属于冗余操作(虽然free(nullptr)安全,但没必要),直接删除该行:
inline void Clean() { struct QueueNode<T>* temp; while (Start != nullptr) { temp = Start; Start = Start->next; free(temp); // 或delete temp } // 删除free(Start); std::cout << "Cleaning queue" << std::endl; }
5. Enqueue模板参数重复定义
Enqueue方法中的template<class T>是多余的,会隐藏外层模板类的T参数,直接删除即可:
// 去掉template<class T> inline void Enqueue(T* Obj) { // 逻辑不变 }
6. 临时对象生命周期风险
测试代码中传入栈上对象t1的指针,虽然当前测试没问题,但如果t1先于队列销毁,队列中的指针会变成悬空指针。如果要存储动态对象,应使用new TestClass(1, "a"),并在释放节点时delete掉data避免内存泄漏。
内容的提问来源于stack exchange,提问作者Bignitse

