双向链表三巨头
Doubly Linked List Big Three
我在尝试让我的复制构造函数、析构函数和赋值运算符为双链表工作时遇到了很多麻烦。
我有一个名为 dlist 的 class 和一个节点 class。节点 class 包含一个私有节点 next 和 previous 以及一个数据字段。我真的被困住了,我无法终生理解如何让这些工作。如果有人甚至只是指出出了什么问题。有时我会遇到段错误,有时会遇到回溯,这取决于我在三巨头中所做的更改。
//Destructor
template<class T>
dlist<T>::~dlist(){
node<T> *rmptr = head;
while(head != NULL && head != tail){
head = head -> next();
delete rmptr;
}
}
//Copy Constructor
template <class T>
dlist<T>::dlist(const dlist<T>& other)
{
if(other.head == NULL){
head = new node<T>;
tail -> set_next(head);
}
else{
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
}
//Assignment Operator
template<class T>
dlist<T>& dlist<T>::operator =(const dlist& other){
if(this == &other){
return *this;
}
node<T> * rmptr;
while(head != NULL){
rmptr = head;
head = head -> next();
delete rmptr;
}
head = tail = NULL;
node<T> *source, *destination;
if(other.head != NULL){
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
return *this;
}
这个 copy/assignment/constructor/destructor 模式是经典的(至少按照我的标准:))
- 首先,您必须将
head
和 tail
设置为 NULL
(或 nullptr
)以避免在对象刚刚创建且未使用时出现段错误。这避免了析构函数读取未初始化的 head
。 NULL
防止析构函数做一些有害的事情
- 其次:创建一个
destroy
实用方法,供析构函数使用(当然),也供赋值运算符使用以释放已分配的对象(否则会发生内存泄漏)。无需 copy/paste 相同的代码。我一开始以为你的删除代码没问题,但事实并非如此。
destroy
看起来像这样(修复了 WhozCraig 评论的帮助):
template<class T>
void dlist<T>::destroy()
{
node<T> *rmptr;
while(head != NULL && head != tail){
rmptr = head;
head = head -> next();
delete rmptr;
}
head = tail = NULL; // set head to NULL again
}
析构函数看起来像这样:
template<class T>
dlist<T>::~dlist(){
destroy();
}
- 第三次创建复制构造函数和赋值运算符使用的
copy
实用方法(它们共享很多相同的代码,不需要copy/paste)
copy
方法如下所示:
template <class T>
void dlist<T>::copy(const dlist<T>& other)
{
if(other.head == NULL){
head = new node<T>;
tail -> set_next(head);
}
else{
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
}
您现在可以非常简单地实现复制构造函数和赋值运算符:
template<class T>
dlist<T>::dlist(const dlist<T>& other)
{
copy(other);
}
//Assignment Operator
template<class T>
dlist<T>& dlist<T>::operator =(const dlist& other){
if(this == &other){
return *this;
}
destroy();
copy(other);
return *this;
}
将一个对象分配给另一个已经 "full"(也称为已分配)对象时,您不会有任何内存泄漏,并且您不会因释放未分配区域而出现段错误,因为在新对象上 head
和 tail
是 NULL
。
对于回答 "beyond" 这个问题,我深表歉意,但这种模式非常有用,未经训练的编码人员尝试像这样编写低级 类 总是会遇到同样的问题 memory-leak/crash/copy-paste 障碍。
我在尝试让我的复制构造函数、析构函数和赋值运算符为双链表工作时遇到了很多麻烦。 我有一个名为 dlist 的 class 和一个节点 class。节点 class 包含一个私有节点 next 和 previous 以及一个数据字段。我真的被困住了,我无法终生理解如何让这些工作。如果有人甚至只是指出出了什么问题。有时我会遇到段错误,有时会遇到回溯,这取决于我在三巨头中所做的更改。
//Destructor
template<class T>
dlist<T>::~dlist(){
node<T> *rmptr = head;
while(head != NULL && head != tail){
head = head -> next();
delete rmptr;
}
}
//Copy Constructor
template <class T>
dlist<T>::dlist(const dlist<T>& other)
{
if(other.head == NULL){
head = new node<T>;
tail -> set_next(head);
}
else{
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
}
//Assignment Operator
template<class T>
dlist<T>& dlist<T>::operator =(const dlist& other){
if(this == &other){
return *this;
}
node<T> * rmptr;
while(head != NULL){
rmptr = head;
head = head -> next();
delete rmptr;
}
head = tail = NULL;
node<T> *source, *destination;
if(other.head != NULL){
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
return *this;
}
这个 copy/assignment/constructor/destructor 模式是经典的(至少按照我的标准:))
- 首先,您必须将
head
和tail
设置为NULL
(或nullptr
)以避免在对象刚刚创建且未使用时出现段错误。这避免了析构函数读取未初始化的head
。NULL
防止析构函数做一些有害的事情 - 其次:创建一个
destroy
实用方法,供析构函数使用(当然),也供赋值运算符使用以释放已分配的对象(否则会发生内存泄漏)。无需 copy/paste 相同的代码。我一开始以为你的删除代码没问题,但事实并非如此。
destroy
看起来像这样(修复了 WhozCraig 评论的帮助):
template<class T>
void dlist<T>::destroy()
{
node<T> *rmptr;
while(head != NULL && head != tail){
rmptr = head;
head = head -> next();
delete rmptr;
}
head = tail = NULL; // set head to NULL again
}
析构函数看起来像这样:
template<class T>
dlist<T>::~dlist(){
destroy();
}
- 第三次创建复制构造函数和赋值运算符使用的
copy
实用方法(它们共享很多相同的代码,不需要copy/paste)
copy
方法如下所示:
template <class T>
void dlist<T>::copy(const dlist<T>& other)
{
if(other.head == NULL){
head = new node<T>;
tail -> set_next(head);
}
else{
head = new node<T>(other.head -> data());
tail = new node<T>;
head -> set_next(tail);
tail -> set_previous(head);
node<T> *source = other.head -> next();
node<T> *destination = head;
while(source != NULL && source != other.tail){
tail -> set_next(new node<T>);
destination -> set_next(tail);
tail -> set_data(source -> data());
tail = tail -> next();
source = source -> next();
}
}
}
您现在可以非常简单地实现复制构造函数和赋值运算符:
template<class T>
dlist<T>::dlist(const dlist<T>& other)
{
copy(other);
}
//Assignment Operator
template<class T>
dlist<T>& dlist<T>::operator =(const dlist& other){
if(this == &other){
return *this;
}
destroy();
copy(other);
return *this;
}
将一个对象分配给另一个已经 "full"(也称为已分配)对象时,您不会有任何内存泄漏,并且您不会因释放未分配区域而出现段错误,因为在新对象上 head
和 tail
是 NULL
。
对于回答 "beyond" 这个问题,我深表歉意,但这种模式非常有用,未经训练的编码人员尝试像这样编写低级 类 总是会遇到同样的问题 memory-leak/crash/copy-paste 障碍。