You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中使用链表实现正确的拷贝构造函数?

如何正确实现链表栈的拷贝构造函数?

你给出的拷贝构造函数写法确实有问题——这是典型的浅拷贝,会导致两个Stack对象共享同一份链表内存,带来严重的内存安全问题。我来给你拆解问题,再一步步写出正确的深拷贝实现。

为什么原写法错误?

// 错误的拷贝构造函数
Stack::Stack(const Stack& obj) {
    head = obj.head;
}

这段代码只是把当前对象的head指针直接指向了传入对象obj的链表头节点。这意味着两个Stack对象会共享同一份链表内存:当其中一个对象被销毁时,它的析构函数会释放整个链表,另一个对象的head就变成了野指针,后续访问或销毁这个对象都会触发未定义行为(比如程序崩溃)。

要解决这个问题,我们需要实现深拷贝——为新对象创建一份完全独立的链表副本,而不是共享原对象的内存。


正确的拷贝构造函数实现

Stack::Stack(const Stack& obj) {
    head = nullptr; // 初始化当前对象的链表为空
    Node* current = obj.head; // 用于遍历原链表的指针
    Node** tail = &head; // 指向当前链表尾部指针的指针,方便添加新节点

    // 遍历原链表,逐个复制节点
    while (current != nullptr) {
        // 创建新节点,复制原节点的item值
        *tail = new Node(current->item);
        // 将tail移动到新节点的next指针地址,准备下一次添加
        tail = &((*tail)->next);
        // 移动原链表的遍历指针
        current = current->next;
    }
}

代码逻辑解释:

  1. 先把当前对象的head初始化为nullptr,确保链表一开始是空的。
  2. 用current指针遍历传入对象obj的整个链表。
  3. 用tail指针跟踪当前新链表的尾部:每次创建一个新节点(复制原节点的item),把它链接到新链表的尾部,然后更新tail的位置,直到遍历完原链表的所有节点。
    这样新对象就拥有了完全独立的链表,和原对象互不干扰。

额外建议:遵循三/五法则

实现了拷贝构造函数后,你还应该实现赋值运算符重载和析构函数(遵循C++的三法则/五法则),避免其他场景下的浅拷贝问题。这里提供两种常见的赋值运算符实现方式:

方式1:手动实现

Stack& Stack::operator=(const Stack& obj) {
    if (this != &obj) { // 防止自赋值导致的内存泄漏
        // 先销毁当前对象的原有链表
        Node* temp = head;
        while (head != nullptr) {
            head = head->next;
            delete temp;
            temp = head;
        }

        // 执行和拷贝构造函数相同的深拷贝逻辑
        head = nullptr;
        Node* current = obj.head;
        Node** tail = &head;
        while (current != nullptr) {
            *tail = new Node(current->item);
            tail = &((*tail)->next);
            current = current->next;
        }
    }
    return *this;
}

方式2:拷贝交换 idiom(更简洁、异常安全)

这种方式利用拷贝构造函数和交换函数来实现,代码更简洁且具备异常安全性:

// 先实现一个交换函数
void Stack::swap(Stack& other) noexcept {
    std::swap(head, other.head);
}

// 赋值运算符:传值调用会先触发拷贝构造,再交换
Stack& Stack::operator=(Stack other) {
    swap(other);
    return *this;
}

当赋值时,other是传入对象的拷贝,交换后当前对象拥有新的链表,而other会在函数结束时销毁,带走原来的旧链表,自动完成内存释放。

内容的提问来源于stack exchange,提问作者swagalistic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:25:42