C++栈类拷贝构造后触发Segmentation fault,析构函数问题求助
问题分析:栈类Segmentation Fault问题排查与解决
问题现象
运行测试代码后,程序输出正常内容后触发Segmentation fault:
#include <iostream> #include <string> #include "Stack.h" using namespace std; int main() { string Text; Stack S1; S1.push("Mary"); S1.push("Apple"); S1.push("Tom"); S1.peek(Text); cout << "Results from peek: " << Text << endl; S1.view(); cout << "Hello"; Stack S2(S1); }
程序输出:
Results from peek: Tom TOP -> Tom -> Apple -> Mary -> BOTTOM Segmentation fault
GDB调试信息:
Program received signal SIGSEGV, Segmentation fault. __GI___libc_free (mem=0x55fa1e0ff3c3c990) at malloc.c:3102 3102 malloc.c: No such file or directory.
Valgrind检测结果(确认问题出在析构函数):
==1236== Conditional jump or move depends on uninitialised value(s) ==1236== at 0x10983D: Stack::~Stack() (in my_folder) ==1236== by 0x1094D1: main (in my_folder) ==1236== ==1236== Use of uninitialised value of size 8 ==1236== at 0x109846: Stack::~Stack() (in my_folder) ==1236== by 0x1094D1: main (in my_folder) ==1236==
栈类定义及核心实现如下:
typedef std::string SElement; class Stack { public: Stack(); Stack( Stack & ); ~Stack(); void push ( const SElement ); void pop( SElement & ); void peek( SElement & ); void view(); private: struct SNode; typedef SNode * SNodePtr; struct SNode { SElement element; SNodePtr next; }; SNodePtr top; };
// 构造函数 Stack::Stack() { top = nullptr; } // 析构函数 Stack::~Stack() { SNodePtr nextNode; while (top != nullptr) { nextNode = top->next; delete top; top = nextNode; } } // push实现 void Stack::push( const SElement Text) { SNodePtr NewNode = new SNode; NewNode->next = top; NewNode->element = Text; top = NewNode; }
原因分析
问题根源在于拷贝构造函数未正确实现:
- 你声明了拷贝构造函数
Stack( Stack & );,但未提供具体逻辑,编译器会自动生成默认拷贝构造函数。默认拷贝是浅拷贝,只会直接复制top指针的地址值,不会复制整个链表节点。 - 当执行
Stack S2(S1);时,S2的top指针与S1的top指向同一块内存区域。 - 程序结束时,S1和S2的析构函数会先后执行:第一个析构函数(如S2的)会释放整个链表的所有节点,此时S1的
top变成悬空指针;第二个析构函数(S1的)访问悬空指针的next成员,触发未初始化值的访问,最终导致free操作时的Segmentation fault。
Valgrind的报错也验证了这一点:析构函数中使用了已失效的未初始化指针。
解决方法
实现深拷贝版本的拷贝构造函数,复制整个链表的所有节点,而非仅复制指针:
// 修改拷贝构造函数参数为const(拷贝不应修改原对象) Stack::Stack(const Stack &other) { top = nullptr; if (other.top == nullptr) { return; } // 复制第一个节点 SNodePtr currentOther = other.top; SNodePtr newNode = new SNode; newNode->element = currentOther->element; newNode->next = nullptr; top = newNode; SNodePtr currentThis = top; // 遍历原栈,复制剩余节点 currentOther = currentOther->next; while (currentOther != nullptr) { SNodePtr temp = new SNode; temp->element = currentOther->element; temp->next = nullptr; currentThis->next = temp; currentThis = temp; currentOther = currentOther->next; } }
同时,遵循C++的三/五法则,补充实现赋值运算符重载,避免后续赋值操作出现同样的浅拷贝问题:
Stack& Stack::operator=(const Stack& other) { // 防止自赋值 if (this == &other) { return *this; } // 先释放当前栈的所有节点 SNodePtr nextNode; while (top != nullptr) { nextNode = top->next; delete top; top = nextNode; } // 深拷贝原栈节点 if (other.top == nullptr) { top = nullptr; return *this; } SNodePtr currentOther = other.top; SNodePtr newNode = new SNode; newNode->element = currentOther->element; newNode->next = nullptr; top = newNode; SNodePtr currentThis = top; currentOther = currentOther->next; while (currentOther != nullptr) { SNodePtr temp = new SNode; temp->element = currentOther->element; temp->next = nullptr; currentThis->next = temp; currentThis = temp; currentOther = currentOther->next; } return *this; }
最后,记得同步修改类声明中的拷贝构造函数参数为const Stack&:
Stack(const Stack &);
内容的提问来源于stack exchange,提问作者cwjensen
相关产品推荐
相关产品推荐

