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

基于指定格式数组创建C++ shared_ptr链表的正确方法及内存问题解决

Creating a circular linked list with shared_ptr from a specific array format in C++

Problem Description

I need to correctly create a C++ linked list using shared_ptr based on a specific array format. The node type is defined as:

template<typename T> struct ListNode {
public:
    T data;
    shared_ptr<ListNode<T>> next;
    ListNode() { }
    ListNode(T data, shared_ptr<ListNode<T>> next) {
        this->data = data;
        this->next = next;
    }
};

The array follows the structure [value1, index1, value2, index2, ... , valueN], where even positions (0-based) hold node values, and the immediately following odd positions hold the relative index of the next node in the array. For example, the array [1,1,2,2,3,1] should create a circular linked list like:
node(val=1,idx=0) -> node(val=2,idx=1) -> node(val=3,idx=2) -> node(val=2,idx=1) -> ...

My current implementation has memory issues (likely circular reference causing memory leaks):

shared_ptr<ListNode<int>> convert(vector<int> value) {
    vector<shared_ptr<ListNode<int>>> nodes;
    for (int i = 0; i < value.size(); i += 2) {
        shared_ptr<ListNode<int>> node = make_shared<ListNode<int>>();
        node->data = value[i];
        nodes.push_back(node);
    }
    for (int i = 1; i < value.size(); i += 2) {
        shared_ptr<ListNode<int>> node = nodes[floor((i - 1) / 2)];
        int nextIndex = value[i];
        if (nextIndex >= 0) {
            shared_ptr<ListNode<int>> nextNode = nodes[nextIndex];
            node->next = nextNode;
        }
    }
    return nodes.empty() ? nullptr : nodes[0];
}

When I call this function, I encounter memory problems. Could you provide a correct implementation?


Solution

The root cause of your memory issues is circular references created by using shared_ptr for the next pointer in cycles. shared_ptr uses reference counting to manage memory—if nodes form a cycle (like your example where node1 points to node2, which points back to node1), their reference counts never drop to zero, leading to permanent memory leaks.

Here's the fixed implementation, which uses weak_ptr to break these cycles while maintaining the required list structure:

Step 1: Update the ListNode Structure

First, modify the next member to be a weak_ptr instead of shared_ptr. This prevents the reference count from being incremented for the next node, breaking the cycle:

template<typename T> struct ListNode {
public:
    T data;
    weak_ptr<ListNode<T>> next; // Swap shared_ptr for weak_ptr
    ListNode() { }
    ListNode(T data, shared_ptr<ListNode<T>> next) {
        this->data = data;
        this->next = next; // weak_ptr can be assigned directly from shared_ptr
    }
};

Step 2: Fixed Conversion Function

Adjust the conversion code to work with weak_ptr, and add small improvements like bounds checking and cleaner integer division:

shared_ptr<ListNode<int>> convert(vector<int> value) {
    vector<shared_ptr<ListNode<int>>> nodes;
    
    // First pass: create all nodes and store them
    for (size_t i = 0; i < value.size(); i += 2) {
        auto node = make_shared<ListNode<int>>();
        node->data = value[i];
        nodes.push_back(node);
    }

    // Second pass: link nodes using weak_ptr
    for (size_t i = 1; i < value.size(); i += 2) {
        size_t node_idx = (i - 1) / 2; // No need for floor()—integer division works here
        auto current_node = nodes[node_idx];
        int next_idx = value[i];

        // Ensure next index is valid to avoid out-of-bounds access
        if (next_idx >= 0 && static_cast<size_t>(next_idx) < nodes.size()) {
            current_node->next = nodes[next_idx];
        }
    }

    return nodes.empty() ? nullptr : nodes[0];
}

How to Use the Next Node

When accessing the next node in the list, you need to lock() the weak_ptr to get a valid shared_ptr (this also checks if the node is still alive):

auto head = convert({1,1,2,2,3,1});
auto current = head;
for (int i = 0; i < 5; ++i) {
    cout << current->data << " ";
    if (auto next_node = current->next.lock()) {
        current = next_node;
    } else {
        break; // Next node no longer exists
    }
}
// Output: 1 2 3 2 2

Key Improvements

  • Cycle safety: weak_ptr breaks circular references, allowing nodes to be properly destroyed when no external shared_ptr references exist.
  • Bounds checking: Prevents crashes from invalid nextIndex values.
  • Cleaner code: Removed redundant floor() call since integer division handles the index calculation correctly.

If modifying the ListNode structure isn't an option (e.g., it's part of a fixed API), manual cycle management is possible but far more error-prone. Using weak_ptr is the standard, safe approach for this scenario.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 21:07:28