【问题标题】:Deep copy of linked list链表的深拷贝
【发布时间】:2014-05-25 00:26:48
【问题描述】:

我有双向链表,m_Terminal1 是链表的开头,m_Terminal2 是链表的结尾。这是我的代码

CBusLine(const CBusLine & orig){
      m_Terminal1 = new TStop(orig.m_Terminal1 -> m_Name);
      m_Terminal2 = new TStop(orig.m_Terminal1 -> m_Name);
      TStop * tmpx = m_Terminal2;
      TStop * copy = orig.m_Terminal1;
      copy = copy ->m_Next;
      while(copy != NULL){
          TStop * copy_tmp = new TStop(copy->m_Name);
          tmpx -> m_Next = copy_tmp;
          copy_tmp -> m_Prev = tmpx;
          tmpx = copy_tmp;
          copy = copy->m_Next;
      }

不幸的是,它不起作用,当我想打印出列表时,只有第一项。 这是源文件http://pastebin.com/YCKyZ31K。有人可以帮我吗?提前谢谢你

【问题讨论】:

  • 如果你只是重新安排你的Add() 函数实际上只是添加数据(而不是做所有的 I/O),那么复制构造函数就变成了 3 或 4 行代码。您需要做的就是在一个循环中调用 Add(),该循环从传入的列表中的第一项到最后一项。
  • 你应该将“CBusLine & orig”设为“const CBusLine & orig”。

标签: c++ deep-copy doubly-linked-list


【解决方案1】:

您正在创建两个与第一个停靠点同名的对象(m_Terminal1m_Terminal2),并将停靠点列表添加到m_Terminal2。因为变量指向内存中的不同位置(由 new 调用提供),所以它们不是同一个项目,最后,m_Terminal2 将拥有完整列表,而 m_Terminal1 将只有第一个停止。在此过程中,您不会将最后一站存储在 m_Terminal2 中。

解决此问题的一种方法是最初仅初始化和存储第一个停靠点,遍历并添加所有停靠点,然后存储(不重新初始化)最后一个停靠点。

CBusLine(CBusLine & orig){
      m_Terminal1 = new TStop(orig.m_Terminal1 -> m_Name);
      TStop * tmpx = m_Terminal1;
      TStop * copy = orig.m_Terminal1;
      copy = copy ->m_Next;
      while(copy != NULL){
          TStop * copy_tmp = new TStop(copy->m_Name);
          tmpx -> m_Next = copy_tmp;
          copy_tmp -> m_Prev = tmpx;
          tmpx = copy_tmp;
          copy = copy->m_Next;
      m_Terminal2 = tmpx;
}

【讨论】:

  • 是的,但起初 m_Terminal2 指向与 m_Terminal1 m_Terminal2 = new TStop(orig.m_Terminal1 -> m_Name); 相同的项目,所以我认为我不会在最后一站之后添加它。我该如何解决?
  • 啊,好的,所以你没有在最后一站之后添加(我会修改我的答案)。但是,m_Terminal2m_Terminal1 指向的项目不同。您正在创建两个不同的对象(由两个 new 调用提供),并将列表添加到其中一个对象中。
【解决方案2】:

您应该重用已经编写的代码。你写了一个Add() 函数,为什么不用它呢?

首先,修复 Add() 函数,使其调用另一个函数,其唯一目的是在序列末尾添加一个节点:

void CBusLine::Add(istream& is)
{   
    string line;
    while(is.good())
    {
        getline(is, line);
        if(line == "")
            continue;
        AddInternal(line); 
    }
}

void CBusLine::AddInternal(string& line)
{
    TStop * n = new TStop(line);
    if(m_Terminal2 == NULL){
        m_Terminal1 = n;
        m_Terminal2 = n;
    }
    else{
        m_Terminal2 -> m_Next = n;
        n -> m_Prev = m_Terminal2;
        m_Terminal2 = n;
    }
}

拆分 Add() 函数的原因是现在,复制构造函数(和赋值运算符)变得非常简单。

CBusLine(const CBusLine & orig)
{
    TStop *startTerm = orig.m_Terminal1;
    while ( startTerm != orig.m_Terminal2 )
    {
       AddInternal( startTerm->m_name);
       startTerm = startTerm->m_Next;
    }
}

当然,AddInternal 必须正常工作。我假设 m_Terminal1 和 m_Terminal2 是链表的开始和结束。

鉴于 AddInternal() 工作正常,复制构造函数是一个简单的循环,从开始到结束终端,并使用终端的字符串数据调用 AddInternal()。这比重新创建您已经在 Add() 函数中编写的代码更简单,并且在逻辑上更有意义。

请注意,除了复制构造函数之外,您还必须编写赋值运算符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 2011-06-08
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多