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

使用shared_ptr实现双向链表时触发读取访问违规异常

问题描述

将双向链表从原始指针改为std::shared_ptr后触发未处理的读取访问违规异常,错误信息如下:

unhandled exception thrown: read access violation. "this" was 0x8
( Access violation reading location 0x0000000000000010. Unhandled exception thrown: read access violation).

原始指针版本可正常运行,推测问题可能出在DoubleLinkedList::addNodeAfter()的nextNode = mypointer-> next;操作或shared_ptr赋值nullptr的问题,但无法确定具体原因。

以下是完整代码:

Node.h

#pragma once
#include <iostream>
#include<memory>

class Node
{
public:
    Node();
    Node(int k, int d);
    int key;
    int data;
    
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> previous;
    //Node* next;
    //Node* previous;
};

doubleLinkedList.h

#pragma once

/*! \class Double Linked List
    \brief A double linked list data structure
*/

#include <iostream>
#include "../Node.h"
#include <string>
#include<memory>

class DoubleLinkedList : public Node
{
private :
    std::shared_ptr<Node> head;             //Node* head; 
    std::shared_ptr<Node> temp;           //Node* temp;
    std::shared_ptr<Node> mypointer;            //Node* ptr;
    std::shared_ptr<Node> nextNode;
    std::shared_ptr<Node> prevNode;

public :
    DoubleLinkedList();
    DoubleLinkedList(std::shared_ptr<Node> n);
    std::shared_ptr<Node> checkNodeExsits(int k); //Node*
    void addNodeToFront(std::shared_ptr<Node> n); //Node*
    void addNodeToEnd(std::shared_ptr<Node>  n);   //Node*
    void addNodeAfter(int k, std::shared_ptr<Node> n); //Node*
    void UpdateNode(int k , int d);
    void deleteNode(int k);
    void printList();
    void printInfo(std::string Info);
};

Node.cpp

#include "Node.h"
#include <iostream>

Node::Node() 
{
    key = 0;
    data = 0;
    next = nullptr;
    previous = nullptr;
}

Node::Node(int k, int d) 
{
    key = k;
    data = d;
}

doubleLinkedList.cpp

#include <iostream>
#include "include\doubleLinkedList.h"

DoubleLinkedList::DoubleLinkedList()
{
    head = nullptr;
}

DoubleLinkedList::DoubleLinkedList(std::shared_ptr<Node> n)
{
     head = n;
}

std::shared_ptr<Node> DoubleLinkedList::checkNodeExsits(int k)
{
     temp = nullptr;
     mypointer = head;

    while (mypointer != nullptr) {
        if (mypointer -> key == k) {
            temp = mypointer;
        }
        
        mypointer = mypointer-> next;
    }

    return temp;
}

void DoubleLinkedList::addNodeToFront(std::shared_ptr<Node> n)
{
    if (checkNodeExsits(n->key) != nullptr) 
    {
        printInfo("Node Already exist with key Number ");
    }
    else {
        if (head == nullptr) {
            head = n;
            printInfo("Node Added as Head Node");
        }
        else {
            head->previous = n;
            n->next = head;
            head = n;
            printInfo("Node Added To The Begining");
        }
    }
}

void DoubleLinkedList::addNodeToEnd(std::shared_ptr<Node> n)
{
    if (checkNodeExsits(n->key) != nullptr) 
    {
        printInfo("Node Already exist with key Number");
    }
    else {
        if (head == nullptr) 
        {
            head = n;  // if there isnt any node in the list.
            printInfo("Node Has Been Added As Head Node");
        }
        else {
            mypointer = head;
            while (mypointer ->next != nullptr)
            {
                mypointer = mypointer->next;
            }
            mypointer->next = n;
            n->previous = mypointer;
            printInfo("Node Has Been Added To The End");
        }
    }
}

void DoubleLinkedList::addNodeAfter(int k, std::shared_ptr<Node> n)
{
    mypointer = checkNodeExsits(k);
    if (mypointer == nullptr) {
        printInfo("No Node Exists With The Key Value");
    }
    else {
        if (checkNodeExsits(n -> key) != nullptr) {
            printInfo("Node Already exist with key Number");
        }
        else {
               nextNode = mypointer-> next;
            // inserting at the end
            if (nextNode == nullptr) {
                mypointer-> next = n;
                n -> previous = mypointer;
                printInfo("Node Inserted at the END");
            }

            //inserting in between
            else {
                n -> next = nextNode;
                nextNode -> previous = n;
                n -> previous = mypointer;
                mypointer-> next = n;
                printInfo("Node Inserted in Between");
            }
        }
    }
}

void DoubleLinkedList::UpdateNode(int k, int d)
{
    mypointer = checkNodeExsits(k);
    if (mypointer != nullptr) {
        mypointer-> data = d;
        std::cout << "Node Data Updated Successfully" << std::endl;
    }
    else {
        std::cout << "Node Doesn't exist with key value : " << k << std::endl;
    }
}

void DoubleLinkedList::deleteNode(int k)
{
    mypointer = checkNodeExsits(k);
    if (mypointer == nullptr) {
        std::cout << "No node exists with key value: " << k << std::endl;
    }
    else {
        if (head -> key == k) {
            head = head -> next;
            std::cout << "Node UNLINKED with keys value : " << k << std::endl;
        }
        else {
             nextNode = mypointer-> next;
             prevNode = mypointer-> previous;
            // deleting at the end
            if (nextNode == nullptr) {
                prevNode -> next = nullptr;
                std::cout << "Node Deleted at the END" << std::endl;
            }

            //deleting in between
            else {
                prevNode -> next = nextNode;
                nextNode -> previous = prevNode;
                std::cout << "Node Deleted in Between" << std::endl;
            }
        }
    }
}

void DoubleLinkedList::printList()
{
    if (head == nullptr) {
        std::cout << "No Nodes in Doubly Linked List";
    }
    else {
        std::cout << std::endl << "Doubly Linked List Values : ";
         temp = head;

        while (temp != nullptr) {
            std::cout << "[Key: " << temp->key << ", Data: " << temp->data << "] <___> " << std::endl;
            temp = temp -> next;
        }
    }
}

void DoubleLinkedList::printInfo(std::string Info)
{
    std::cout << Info << std::endl;
}

main.cpp

#include <iostream>
#include "../include/doubleLinkedList.h"
#include"../Node.h"

void Print(std::string info)
{
    std::cout << info << std::endl;
}

int main() {
    DoubleLinkedList myNode;
    //Node* newNode = new Node(2,7);
    std::shared_ptr<Node> newNode = std::make_shared<Node>(2, 7); // enter key number and data number
    std::shared_ptr<Node> newNode1 = std::make_shared<Node>(3, 9);// enter key number and data number

    newNode->key;
    newNode->data;
    myNode.addNodeToFront(newNode);

    newNode->key;
    newNode->data;
    myNode.addNodeAfter(2, newNode1); // enter the key number of existing node and then to add new key number and new data
    myNode.printList();

    system("pause");
    return 0;
}

问题分析与解决

1. 直接触发异常的原因:Node带参构造函数未初始化指针成员

Node(int k, int d)构造函数只初始化了key和data,未给next和previous赋值为nullptr。用std::make_shared<Node>(2,7)创建节点时,这两个shared_ptr的内部原始指针是未定义的垃圾值,后续访问mypointer->next时等同于访问野指针,直接触发读取访问违规。

修复代码:

Node::Node(int k, int d) 
{
    key = k;
    data = d;
    next = nullptr;
    previous = nullptr;
}

2. 设计错误:DoubleLinkedList不应继承Node

链表是包含节点的容器,不是节点本身。继承Node会让链表对象自带Node的冗余成员(key、data、next、previous),无意义且可能引发意外内存访问问题。

修复代码:

// doubleLinkedList.h
class DoubleLinkedList // 移除 : public Node
{
    // 原有代码不变
};

3. 代码隐患:成员变量用作临时遍历指针

DoubleLinkedList的temp、mypointer等是类成员变量,多线程环境或连续调用成员函数时会导致状态混乱。应改为函数内局部变量,避免交叉污染。

示例修复(以checkNodeExsits为例):

std::shared_ptr<Node> DoubleLinkedList::checkNodeExsits(int k)
{
    std::shared_ptr<Node> temp = nullptr; // 局部变量
    std::shared_ptr<Node> mypointer = head; // 局部变量

    while (mypointer != nullptr) {
        if (mypointer->key == k) {
            temp = mypointer;
        }
        mypointer = mypointer->next;
    }

    return temp;
}

同理,其他函数内的temp、mypointer等都要改为局部变量,并删除类内对应的成员声明。

4. 潜在问题:shared_ptr循环引用

双向链表中,节点的next和previous都是shared_ptr会形成循环引用(A->next指向B,B->previous指向A),导致节点无法自动释放,造成内存泄漏。

解决方法:将previous改为std::weak_ptr<Node>打破循环:

// Node.h
class Node
{
public:
    // ...
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> previous; // 替换为weak_ptr
};

同时修改所有涉及previous的操作,比如:

// addNodeToFront中
head->previous = std::weak_ptr<Node>(n);
n->next = head;

// 访问previous时需要lock()
if (auto prev_node = some_node->previous.lock()) {
    prev_node->next = ...;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 10:05:33