双链表三巨头

Doubly Linked List Big Three

提问人:Cody 提问时间:10/25/2016 更新时间:10/25/2016 访问量:1427

问:

我在尝试让我的复制构造函数、析构函数和赋值运算符为双链表工作时遇到了很多麻烦。 我有一个名为 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;
}
C++ 析构函数 复制构造函数 doublebly-linked-list assignment-operator

评论

2赞 WhozCraig 10/25/2016
老实说,即使在你进入复制语义之前,这段代码也有几件事是错误的。例如:析构函数重复删除相同的指针(初始化为列表头),并泄漏分配给列表的其余内存(假设您没有崩溃,这很有可能)。rmptr
0赞 Cody 10/25/2016
感谢您的投入。这也很有帮助。

答:

0赞 Jean-François Fabre 10/25/2016 #1

这种复制/赋值/构造函数/析构函数模式是经典的(至少按照我的标准:))

  • 首先,您必须设置 和 to(或 ),以避免在对象刚刚创建且未使用时出现段错误。这样可以避免析构函数读取未初始化的 . 防止析构函数执行有害操作headtailNULLnullptrheadNULL
  • 第二:创建一个由析构函数(当然)和赋值运算符使用的实用程序方法来释放赋值对象(否则会出现内存泄漏)。无需复制/粘贴相同的代码。我首先假设您的删除代码没问题,但事实并非如此。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;
}

当将一个对象分配给另一个已经“满”(又称已分配)的对象时,您不会有任何内存泄漏,并且您不会因为释放未分配的区域而出现段错误,因为在新对象上并且是 .headtailNULL

我很抱歉回答了“超越”这个问题,但这种模式非常有用,未经训练的编码人员试图编写这样的低级类代码时,总是会遇到同样的内存泄漏/崩溃/复制粘贴障碍。

评论

0赞 Cody 10/25/2016
祝福你的灵魂!非常感谢!
1赞 WhozCraig 10/25/2016
@KevinSwagger或使用复制/交换惯用语模型
0赞 Jean-François Fabre 10/25/2016
@WhozCraig,我以为我很聪明!谢谢你。很有意思。