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

递归链表:所有对象均调用析构函数问题咨询

Alright, let's figure out why you're seeing all those extra destructor calls and fix this recursive linked list properly.

First, Let's Spot the Core Issues

Looking at your code, there are a few critical problems causing the excessive destructor invocations:

  1. Type Mismatch & Invalid Initialization
    Your cur is declared as int, but you're trying to assign nullptr to it in the constructor — that's a type error (nullptr is a pointer, not an integer). Plus, you're using new int to assign to cur, which confirms you actually intended cur to be an int* (pointer to int), not a raw int.

  2. Shallow Copy Disaster
    Your cons function returns *this by value, which triggers the default copy constructor. The default copy does a shallow copy: it copies the pointer values of cur and nxt instead of creating new copies of the data they point to. This means multiple List objects end up sharing the same memory addresses. When each of these copies is destroyed, they all try to free the same memory, leading to repeated destructor calls (and likely crashes from double-free errors).

  3. Unimplemented Memory Cleanup
    Your destructor only prints a message — it doesn't free the dynamically allocated cur or recursively delete the nxt node. This would cause memory leaks even if the destructor wasn't being called extra times.


Fixed Implementation

Here's a corrected version of your code with explanations of key changes:

#include <iostream>
#include <utility> // For std::move

class List {
    int* cur;  // Fixed: changed to int* to match your dynamic allocation logic
    List* nxt;
public:
    // Default constructor: initialize pointers to nullptr with initializer list
    List() : cur(nullptr), nxt(nullptr) {}

    // Constructor for a single node with a value
    explicit List(int val) : cur(new int(val)), nxt(nullptr) {}

    // Destructor: properly free dynamic memory and recursively delete next nodes
    ~List() {
        delete cur;       // Free the int stored in this node
        delete nxt;       // Recursively destroy the next node
        std::cout << "Destroyed\n";
    }

    // Disable default copy constructor/assignment to prevent shallow copies
    List(const List&) = delete;
    List& operator=(const List&) = delete;

    // Move constructor: transfer ownership of resources instead of copying
    List(List&& other) noexcept : cur(other.cur), nxt(other.nxt) {
        // Null out the original object's pointers so it doesn't free the resources we took
        other.cur = nullptr;
        other.nxt = nullptr;
    }

    // Move assignment operator
    List& operator=(List&& other) noexcept {
        if (this != &other) {
            // Free current resources first
            delete cur;
            delete nxt;

            // Transfer ownership from other to this
            cur = other.cur;
            nxt = other.nxt;

            // Null out other's pointers
            other.cur = nullptr;
            other.nxt = nullptr;
        }
        return *this;
    }

    // Fixed cons: return a reference instead of a value to avoid copying
    List& cons(const int& a) {
        if (cur == nullptr) {
            cur = new int(a);
        } else {
            // Create a new node to hold the current value
            List* new_node = new List;
            new_node->cur = cur;
            new_node->nxt = nxt;

            // Update current node to hold the new value
            cur = new int(a);
            nxt = new_node;
        }
        return *this; // Return reference to avoid copy
    }

    // Friend declaration for ostream operator
    friend std::ostream& operator<<(std::ostream& os, const List& list);
};

// Implement the ostream operator to print the list
std::ostream& operator<<(std::ostream& os, const List& list) {
    const List* current = &list;
    while (current != nullptr && current->cur != nullptr) {
        os << *(current->cur);
        if (current->nxt != nullptr && current->nxt->cur != nullptr) {
            os << " -> ";
        }
        current = current->nxt;
    }
    return os;
}

// Test the implementation
int main() {
    List my_list;
    my_list.cons(3).cons(2).cons(1);
    std::cout << my_list << "\n"; // Output: 1 -> 2 -> 3
    return 0;
}

Key Fixes Explained

  • Return References Instead of Values: The cons function now returns List& instead of List, so no copy of the List object is created when you chain calls like cons(3).cons(2).
  • Disable Shallow Copies: We deleted the default copy constructor and assignment operator to prevent accidental shallow copies that cause double-free errors.
  • Proper Destructor: The destructor now recursively frees all nodes and the int values they hold, ensuring no memory leaks and only one destructor call per node.
  • Move Semantics: Added move constructor/assignment to allow efficient transfer of List objects without copying, which is useful if you need to pass or return lists elsewhere.

Simplified Alternative (No Dynamic Int)

If you don't need to dynamically allocate the int (which is probably unnecessary here), you can simplify the code even more by making cur a raw int and using std::optional to track empty states:

#include <iostream>
#include <optional>
#include <utility>

class List {
    std::optional<int> cur;
    List* nxt;
public:
    List() : cur(std::nullopt), nxt(nullptr) {}
    explicit List(int val) : cur(val), nxt(nullptr) {}

    ~List() {
        delete nxt;
        std::cout << "Destroyed\n";
    }

    List(const List&) = delete;
    List& operator=(const List&) = delete;
    List(List&& other) noexcept : cur(std::move(other.cur)), nxt(other.nxt) {
        other.nxt = nullptr;
    }
    List& operator=(List&& other) noexcept {
        if (this != &other) {
            delete nxt;
            cur = std::move(other.cur);
            nxt = other.nxt;
            other.nxt = nullptr;
        }
        return *this;
    }

    List& cons(const int& a) {
        if (!cur.has_value()) {
            cur = a;
        } else {
            List* new_node = new List(*cur);
            new_node->nxt = nxt;
            cur = a;
            nxt = new_node;
        }
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const List& list);
};

std::ostream& operator<<(std::ostream& os, const List& list) {
    const List* current = &list;
    while (current != nullptr && current->cur.has_value()) {
        os << current->cur.value();
        if (current->nxt != nullptr && current->nxt->cur.has_value()) {
            os << " -> ";
        }
        current = current->nxt;
    }
    return os;
}

This version eliminates the need to manage dynamic int allocations, reducing the chance of errors.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:25:57