尝试为链表创建复制构造函数时读取访问冲突
Read access violation when trying to create copy constructor for linked list
我在尝试创建复制构造函数的链表实现方面遇到问题。
// Copy Constructor
List342(const List342& source)
{
*this = source;
}
List342& operator=(const List342& source)
{
Node<T>* s_node = nullptr; // Source node
Node<T>* d_node = nullptr; // Destination node
if (this == &source)
{
return *this;
}
// Empty memory on destination
DeleteList();
// If the source is empty, return the current object.
if (source.head_ == nullptr)
{
return *this;
}
// Copy source node to destination node, then make destination node the head.
d_node = new Node<T>;
d_node->data = (source.head_)->data;
head_ = d_node;
s_node = (source.head_)->next;
// Loop and copy the nodes from source
while (s_node != nullptr)
{
d_node->next = new Node<T>;
d_node = d_node-> next;
d_node->data = s_node->data;
s_node = s_node->next;
}
return *this;
}
出于某种原因,VS Studio 在 d_node->data = s_node->data
行向我抛出读取访问冲突,尽管 while
循环试图阻止这种情况。
罪魁祸首可能在 DeleteList
,但由于某些原因,我的其他方法(如打印链表)在调用 DeleteList
后没有任何问题,因为它什么也不打印。我只是想知道这个 DeleteList
方法是否有任何缺陷。
// Delete all elements in the linked list
// Not only do you need to delete your nodes,
// But because the Node data is a pointer, this must be deleted too
void DeleteList()
{
// Similar to the linkedlist stack pop method except you're running a while
// loop until you the is empty.
Node<T>* temp;
while (head_ != nullptr)
{
temp = head_;
head_ = head_->next;
// For some reason if I try to delete temp->data, I keep getting symbols not loaded or debug
// assertion errors
// delete temp->data;
// What I can do here is set it to null
// Then delete it. This may have to do how uninitialized variables have random memory assigned
temp->data = nullptr;
delete temp->data;
delete temp;
}
}
这里是 Node
定义:
template <class T>
struct Node
{
T* data;
//string* data;
Node* next;
}
通过将 Node::data
声明为指针,您的代码负责遵循 Rule of 3/5/0 以正确管理 data
指针。但它并没有这样做。您的复制赋值运算符浅复制指针本身,而不是深复制它们指向的对象。
因此,DeleteList()
在 delete temp->data;
语句上崩溃,因为您最终有多个 Node
指向内存中的相同对象,破坏了唯一的所有权语义。当一个 Node
被销毁时,删除它的 data
对象,从它复制的任何其他 Node
现在留下一个指向无效内存的悬空指针。
如果必须为 Node::data
使用指针,则需要使用 new
单独复制构造每个 data
对象,以便 DeleteList()
稍后可以 delete
他们单独,例如:
d_node = new Node<T>;
d_node->data = new T(*(source.head_->data)); // <--
...
d_node->next = new Node<T>;
d_node = d_node->next;
d_node->data = new T(*(s_node->data)); // <--
...
但是,如果您只是让 Node::data
一开始就不是指针,就不再需要了:
template <class T>
struct Node
{
T data; //string data;
Node* next;
}
话虽如此,您的复制构造函数正在调用您的复制赋值运算符,但 head_
成员尚未初始化(除非已经初始化,而您只是没有显示)。在无效列表上调用 DeleteList()
是 未定义的行为 。使用复制构造函数(所谓的copy-swap idiom)实现赋值运算符会更安全,而不是相反,例如:
// Copy Constructor
List342(const List342& source)
: head_(nullptr)
{
Node<T>* s_node = source.head_;
Node<T>** d_node = &head_;
while (s_node)
{
*d_node = new Node<T>;
(*d_node)->data = new T(*(s_node->data));
// or: (*d_node)->data = s_node->data;
// if data is not a pointer anymore...
s_node = s_node->next;
d_node = &((*d_node)->next);
}
}
List342& operator=(const List342& source)
{
if (this != &source)
{
List342 temp(source);
std::swap(head_, temp.head_);
}
return *this;
}
但是,如果您确保在调用 operator=
,例如:
// Copy Constructor
List342(const List342& source)
: head_(nullptr) // <--
{
*this = source;
}
或者:
// Default Constructor
List342()
: head_(nullptr) // <--
{
}
// Copy Constructor
List342(const List342& source)
: List342() // <--
{
*this = source;
}
或者,直接在 class 声明中初始化 head_
,根本不在构造函数中:
template<typename T>
class List342
{
...
private:
Node<T> *head_ = nullptr; // <--
...
};
我在尝试创建复制构造函数的链表实现方面遇到问题。
// Copy Constructor
List342(const List342& source)
{
*this = source;
}
List342& operator=(const List342& source)
{
Node<T>* s_node = nullptr; // Source node
Node<T>* d_node = nullptr; // Destination node
if (this == &source)
{
return *this;
}
// Empty memory on destination
DeleteList();
// If the source is empty, return the current object.
if (source.head_ == nullptr)
{
return *this;
}
// Copy source node to destination node, then make destination node the head.
d_node = new Node<T>;
d_node->data = (source.head_)->data;
head_ = d_node;
s_node = (source.head_)->next;
// Loop and copy the nodes from source
while (s_node != nullptr)
{
d_node->next = new Node<T>;
d_node = d_node-> next;
d_node->data = s_node->data;
s_node = s_node->next;
}
return *this;
}
出于某种原因,VS Studio 在 d_node->data = s_node->data
行向我抛出读取访问冲突,尽管 while
循环试图阻止这种情况。
罪魁祸首可能在 DeleteList
,但由于某些原因,我的其他方法(如打印链表)在调用 DeleteList
后没有任何问题,因为它什么也不打印。我只是想知道这个 DeleteList
方法是否有任何缺陷。
// Delete all elements in the linked list
// Not only do you need to delete your nodes,
// But because the Node data is a pointer, this must be deleted too
void DeleteList()
{
// Similar to the linkedlist stack pop method except you're running a while
// loop until you the is empty.
Node<T>* temp;
while (head_ != nullptr)
{
temp = head_;
head_ = head_->next;
// For some reason if I try to delete temp->data, I keep getting symbols not loaded or debug
// assertion errors
// delete temp->data;
// What I can do here is set it to null
// Then delete it. This may have to do how uninitialized variables have random memory assigned
temp->data = nullptr;
delete temp->data;
delete temp;
}
}
这里是 Node
定义:
template <class T>
struct Node
{
T* data;
//string* data;
Node* next;
}
通过将 Node::data
声明为指针,您的代码负责遵循 Rule of 3/5/0 以正确管理 data
指针。但它并没有这样做。您的复制赋值运算符浅复制指针本身,而不是深复制它们指向的对象。
因此,DeleteList()
在 delete temp->data;
语句上崩溃,因为您最终有多个 Node
指向内存中的相同对象,破坏了唯一的所有权语义。当一个 Node
被销毁时,删除它的 data
对象,从它复制的任何其他 Node
现在留下一个指向无效内存的悬空指针。
如果必须为 Node::data
使用指针,则需要使用 new
单独复制构造每个 data
对象,以便 DeleteList()
稍后可以 delete
他们单独,例如:
d_node = new Node<T>;
d_node->data = new T(*(source.head_->data)); // <--
...
d_node->next = new Node<T>;
d_node = d_node->next;
d_node->data = new T(*(s_node->data)); // <--
...
但是,如果您只是让 Node::data
一开始就不是指针,就不再需要了:
template <class T>
struct Node
{
T data; //string data;
Node* next;
}
话虽如此,您的复制构造函数正在调用您的复制赋值运算符,但 head_
成员尚未初始化(除非已经初始化,而您只是没有显示)。在无效列表上调用 DeleteList()
是 未定义的行为 。使用复制构造函数(所谓的copy-swap idiom)实现赋值运算符会更安全,而不是相反,例如:
// Copy Constructor
List342(const List342& source)
: head_(nullptr)
{
Node<T>* s_node = source.head_;
Node<T>** d_node = &head_;
while (s_node)
{
*d_node = new Node<T>;
(*d_node)->data = new T(*(s_node->data));
// or: (*d_node)->data = s_node->data;
// if data is not a pointer anymore...
s_node = s_node->next;
d_node = &((*d_node)->next);
}
}
List342& operator=(const List342& source)
{
if (this != &source)
{
List342 temp(source);
std::swap(head_, temp.head_);
}
return *this;
}
但是,如果您确保在调用 operator=
,例如:
// Copy Constructor
List342(const List342& source)
: head_(nullptr) // <--
{
*this = source;
}
或者:
// Default Constructor
List342()
: head_(nullptr) // <--
{
}
// Copy Constructor
List342(const List342& source)
: List342() // <--
{
*this = source;
}
或者,直接在 class 声明中初始化 head_
,根本不在构造函数中:
template<typename T>
class List342
{
...
private:
Node<T> *head_ = nullptr; // <--
...
};