C++ 困惑于如何做双链接列表的深度复制?

C++ 困惑于如何做双链接列表的深度复制?,c++,visual-studio,doubly-linked-list,deep-copy,C++,Visual Studio,Doubly Linked List,Deep Copy,我一直在尝试实现一个双链接列表的深度副本,但我一直遇到问题。我不得不做了几次不同的操作,但最终我得到了一个地址错误。我只需要解释一下如何正确地做 清单H 类列表 { 公众: List(); ~List(); 列表(const List&c); 列表和运算符=(常量列表和t); 私人: 列表*下一步; 列表*Prev; 节点*头; List.cpp List::~List() { 节点*移动=头部; while(移动!=NULL) { 节点*temp=move->Next; 删除移动; 移动=温

我一直在尝试实现一个双链接列表的深度副本,但我一直遇到问题。我不得不做了几次不同的操作,但最终我得到了一个地址错误。我只需要解释一下如何正确地做

清单H

类列表
{
公众:
List();
~List();
列表(const List&c);
列表和运算符=(常量列表和t);
私人:
列表*下一步;
列表*Prev;
节点*头;
List.cpp

List::~List()
{
节点*移动=头部;
while(移动!=NULL)
{
节点*temp=move->Next;
删除移动;
移动=温度;
}
}
列表::列表(常量列表和c)
{
name=c.name;
如果(c.Head==NULL){
Head=NULL;
}
否则{
Head=新节点(*c.Head);
节点*电流=头部;
节点*ObjHead=c.头部;
节点*CurrentObj=ObjHead;
while(当前->下一步!=NULL){
当前->下一步=新节点(当前对象->下一步->调味品);
}
}
}

您可以首先使用一个虚拟头。完成深度复制后,您可以将
头设置为虚拟头的下一个,并删除虚拟头。这样,您也不必检查
是否(c.head==NULL)

    Node *ptr1, *ptr2;
    head = ptr1 = new Node();
    ptr2 = c.head;
    while (ptr2)
    {
        ptr1->next = new Node(*ptr2);
        ptr1 = ptr1->next;
        ptr2 = ptr2->next;
    }

    Node *temp = head;
    head = head->next;
    delete temp;

您可以首先使用虚拟头。完成深度复制后,您可以将
head
设置到虚拟头的下一个,并删除虚拟头。这样,您也不必检查
是否(c.head==NULL)

    Node *ptr1, *ptr2;
    head = ptr1 = new Node();
    ptr2 = c.head;
    while (ptr2)
    {
        ptr1->next = new Node(*ptr2);
        ptr1 = ptr1->next;
        ptr2 = ptr2->next;
    }

    Node *temp = head;
    head = head->next;
    delete temp;

复制链接列表涉及三件事:

  • 遍历正在复制的列表
  • 从原始节点复制新节点
  • 对于(2)中的每个新节点,尾部将其链接到链接列表
  • 第一种是琐碎的,第二种是相当基本的,但第三种是经常让人产生循环的方法。对于复制ctor,一种方法是使用指向指针的指针。这允许我们通过自己的地址来寻址链接列表中的每个指针

    List::List(const List& c)
        : Head(nullptr)
        , name(c.name)
    {
        Node *prev = nullptr;
        Node **pp = &Head;
    
        for (const Node *p = c.Head; p; p = p->Next)
        {
            // make a new node, storing its address at the
            // pointer obtained by dereference of `pp`. the
            // first iteration that will be the Head member.
            *pp = new Node(*p);
            (*pp)->Prev = prev;
            prev = *pp;
    
            // now just advance `pp` to point to the `Next`
            // member of the node we just hung on the list.
            pp = &(*pp)->Next; 
        }
        *pp = nullptr; // terminate the list.
    }
    

    这假设您是
    节点
    类支持复制构造(最好是这样)。但仅此而已。从中,您可以使用来制造复制分配运算符,并具有三个符合性列表类的基本规则。

    复制链接列表涉及三件事:

  • 遍历正在复制的列表
  • 从原始节点复制新节点
  • 对于(2)中的每个新节点,尾部将其链接到链接列表
  • 第一种是琐碎的,第二种是相当基本的,但第三种是经常让人产生循环的方法。对于复制ctor,一种方法是使用指向指针的指针。这允许我们通过自己的地址来寻址链接列表中的每个指针

    List::List(const List& c)
        : Head(nullptr)
        , name(c.name)
    {
        Node *prev = nullptr;
        Node **pp = &Head;
    
        for (const Node *p = c.Head; p; p = p->Next)
        {
            // make a new node, storing its address at the
            // pointer obtained by dereference of `pp`. the
            // first iteration that will be the Head member.
            *pp = new Node(*p);
            (*pp)->Prev = prev;
            prev = *pp;
    
            // now just advance `pp` to point to the `Next`
            // member of the node we just hung on the list.
            pp = &(*pp)->Next; 
        }
        *pp = nullptr; // terminate the list.
    }
    


    这假设您是
    节点
    类支持复制构造(最好是)。但这就是所需要的。从那以后,您可以使用来制造副本分配运算符,并有一个三合规列表类的基本规则。

    首先对我来说似乎是不正确的设计。为什么您有
    list*prev,*next
    ?它们应该是
    Node*prev,*next
    。实际上,它是一个列表的链接列表,不是节点。我有两个双链接列表。节点管理一个数据的双链接列表。然后列表管理节点。首先,对我来说似乎是不正确的设计。为什么有
    list*prev,*next
    ?它们应该是
    Node*prev,*next
    。实际上,它是一个列表的链接列表,而不是节点。我有两个双链接列表d list.Node管理一个双链接的数据列表。然后list管理节点。@lucieon当我运行它并尝试从节点获取数据时,我会抛出一个异常。读取访问冲突。这是nullptr。不确定为什么会发生这种情况。我看不到这种情况会尝试读取
    nullptr
    。尝试使函数
    const
    像这样
    List&operator=(const List&t)const;
    。还要确保在
    节点
    类的默认构造函数中初始化指向
    nullptr
    的指针。@luceion是的,我确保所有指针都设置为nullptr。我非常确定问题出在副本分配的某个地方。请尝试通过调试器运行它。@luceion当我运行它并尝试从节点获取数据时e我遇到一个异常。读取访问冲突。这是nullptr。不确定为什么会发生这种情况。我看不出有这种情况会尝试读取
    nullptr
    。请尝试使函数
    const
    像这样
    List&operator=(const List&t)const;
    。还要确保在
    节点
    类的默认构造函数中初始化指向
    nullptr
    的指针。@luceion是的,我确保所有指针都设置为nullptr。我非常确定问题出在复制分配的某个地方。尝试通过调试器运行它。我的复制构造函数现在工作正常,但是,复制assignment是我遇到一些问题的地方。我试图在没有复制/交换方法的情况下完成它。使用
    Insert()不是更好吗
    并从复制构造函数调用它?@TanveerBadar否。我只得到了关于此列表的信息,没有尾部指针。
    Insert
    因此必须迭代到每次插入结束,因此将复制ctor操作转换为O(N^2),而不是上面看到的非常琐碎的O(N)。如果你想“更好”,它会“更好”为了不做任何疯狂的事情,只需首先使用
    std::list
    。@WhozCraig是的,我确信使用std::list很容易,但我需要在没有它或任何其他容器的情况下实现它。所以据我所知,我还需要在复制赋值操作符中遍历列表。@Jay,你需要实现一个,是的。你需要在我的回答中,我的复制构造函数现在工作得很好,但是,复制赋值是我遇到一些问题的地方。我试图在没有复制/交换方法的情况下完成它。这不是很困难吗