提问人:Cody 提问时间:10/25/2016 更新时间:10/25/2016 访问量:1427
双链表三巨头
Doubly Linked List Big Three
问:
我在尝试让我的复制构造函数、析构函数和赋值运算符为双链表工作时遇到了很多麻烦。 我有一个名为 dlist 的类和一个节点类。Node 类包含一个 next 和 previous 的私有节点和一个数据字段。我真的被困住了,我一辈子都无法理解如何让这些工作。如果有人甚至只是指出问题所在。有时我会得到一个 seg 错误,有时我会得到回溯,这取决于我在三巨头中改变了什么。
//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;
}
答:
0赞
Jean-François Fabre
10/25/2016
#1
这种复制/赋值/构造函数/析构函数模式是经典的(至少按照我的标准:))
- 首先,您必须设置 和 to(或 ),以避免在对象刚刚创建且未使用时出现段错误。这样可以避免析构函数读取未初始化的 . 防止析构函数执行有害操作
head
tail
NULL
nullptr
head
NULL
- 第二:创建一个由析构函数(当然)和赋值运算符使用的实用程序方法来释放赋值对象(否则会出现内存泄漏)。无需复制/粘贴相同的代码。我首先假设您的删除代码没问题,但事实并非如此。
destroy
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
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;
}
当将一个对象分配给另一个已经“满”(又称已分配)的对象时,您不会有任何内存泄漏,并且您不会因为释放未分配的区域而出现段错误,因为在新对象上并且是 .head
tail
NULL
我很抱歉回答了“超越”这个问题,但这种模式非常有用,未经训练的编码人员试图编写这样的低级类代码时,总是会遇到同样的内存泄漏/崩溃/复制粘贴障碍。
评论
0赞
Cody
10/25/2016
祝福你的灵魂!非常感谢!
1赞
WhozCraig
10/25/2016
@KevinSwagger或使用复制/交换惯用语模型。
0赞
Jean-François Fabre
10/25/2016
@WhozCraig,我以为我很聪明!谢谢你。很有意思。
评论
rmptr