C++自定义栈类实现触发std::logic_error异常的问题求助
修复你的C++栈实现中的指针错误与内存异常问题
嘿,第一次尝试用C++实现栈已经很棒了!咱们直接看问题出在哪:
你的核心问题出在myStackElement::popped函数的参数传递上——这个函数里的head是传值参数,你在函数内部修改head = prev,只会改变局部变量的值,完全不会影响myStack类里的成员变量head。这就导致执行pop之后,myStack的head仍然指向已经被delete的节点,变成了野指针。后续调用peek时,你访问的是已经释放的内存里的Data成员,这就触发了字符串构造的异常(也就是你看到的std::logic_error)。
一步步修正你的代码
我们把栈的管理逻辑还给myStack类,让myStackElement只负责存储数据和关联关系,这样逻辑更清晰,也能避免指针混乱:
#include <iostream> #include <string> // 建议不要用using namespace std,避免命名冲突 // using namespace std; class myStackElement { private: std::string Data; myStackElement* prev; public: // 用初始化列表代替构造函数内赋值,更高效 myStackElement(std::string myData, myStackElement* head) : Data(std::move(myData)), prev(head) {} // 只提供获取数据和前一个节点的接口 std::string getValue() const { return Data; } myStackElement* getPrev() const { return prev; } }; class myStack { private: myStackElement* head; public: // 用nullptr代替NULL,类型更安全 myStack() : head(nullptr) {} void push(std::string myData) { myStackElement* temp = new myStackElement(std::move(myData), head); head = temp; } std::string pop() { if (head == nullptr) { // 返回空字符串比"0"更合理,也可以选择抛出异常 return ""; } else { myStackElement* temp = head; std::string poppedValue = temp->getValue(); // 直接在myStack里更新head指针,这才是正确的操作! head = temp->getPrev(); delete temp; // 现在删除的是旧节点,head已经指向新的栈顶了 return poppedValue; } } std::string peek() { if (head == nullptr) { return ""; } else { return head->getValue(); } } // 必须添加析构函数,避免内存泄漏 ~myStack() { while (head != nullptr) { myStackElement* temp = head; head = head->getPrev(); delete temp; } } }; int main() { myStack hello; hello.push("other"); std::string top = hello.peek(); std::cout << top << std::endl; hello.push("another"); top = hello.peek(); std::cout << top << std::endl; std::string popped = hello.pop(); std::cout << popped << std::endl; top = hello.peek(); std::cout << top << std::endl; return 0; }
关键修改点说明
- 修正指针更新逻辑:把栈顶指针的更新逻辑移到
myStack::pop函数里,直接通过temp->getPrev()获取前一个节点,再更新head,这样就不会出现野指针了。 - 简化元素类职责:
myStackElement只负责存储数据和关联前一个节点,不再干预栈的管理,符合单一职责原则。 - 修复内存泄漏:添加了析构函数,遍历整个栈并删除所有节点,避免程序结束后残留未释放的内存。
- 现代C++规范优化:用
nullptr代替NULL,用初始化列表构造对象,用std::move减少字符串拷贝。
现在运行代码会得到正确的输出:
other another another other
而且不会再触发任何异常。
额外小建议
- 可以把
myStackElement设为myStack的内部类,这样外部代码无法直接访问它,封装性更好。 - 可以把
myStack改成模板类,让它支持存储任意类型的数据(比如int、double),而不只是std::string。
内容的提问来源于stack exchange,提问作者mr.loop
相关产品推荐
相关产品推荐

