使用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

