单链表 - popback功能

时间:2016-01-25 19:43:05

标签: c++ linked-list singly-linked-list

我正在尝试编写一个弹出列表中最后一个元素的函数,但是我一直错误地指向非法的de引用。我在这做错了什么?从插入函数中获取分段错误,代码就在底部。任何帮助表示赞赏。

这是我的PopBack功能

template < typename T >
void List<T>::PopBack()
{
  if (Empty())
  {
    std::cerr << "** List error: PopBack() called on empty list\n";
    return;
  }

  Link *oldLink = last_;

  if (first_ == last_)
    first_ = last_ = 0;

    Link *currLink = GetPred(last_);

    last_ = currLink;
    last_->next_ = nullptr;

    delete oldLink;

} // end PopBack()

此处还有我正在调用的功能

template <typename T >
typename List<T>::Link* List<T>::GetPred ( Link* x )
{
  Link* p = first_;
  while ( p != nullptr && p->next_ != x )
    p = p->next_;
  return p;
}

这是我的插入功能

// Insert t at (in front of) i; return i at new element
template < typename T >
ListIterator<T> List<T>::Insert (ListIterator<T> i, const T& t)
{
  if (Empty())  // always insert
  {
    i = End();
  }
  if (!i.Valid()) // null
  {
    std::cerr << " ** cannot insert at position -1\n";
    return End();
  }
  Link* newLink = NewLink(t);
  Link* currLink = GetPred(i.curr_);

  // leave i at new entry and return
  newLink = currLink;
  return i;
}

这是我的结束功能

// return iterator "1 past the back"
template < typename T >
ListIterator<T>  List<T>::End()
{
  Iterator i(last_->next_);
  return i;
}

这是我的有效功能

// test cursor for legal dereference
template < typename T >
bool ConstListIterator<T>::Valid() const
{
  return curr_ != nullptr;
}

这是我的空函数

template < typename T >
bool List<T>::Empty()  const
{
  return (first_ == nullptr);
}

检索方法

template < typename T >
T&  ConstListIterator<T>::Retrieve() const
// Return reference to current t
// note conflicted signature - const method returns non-const reference
{
  if (curr_ == nullptr)
  {
    std::cerr << "** Error: ConstListIterator<T>::Retrieve() invalid dereference\n";
    exit (EXIT_FAILURE);
  }
  return curr_->Tval_;
}

2 个答案:

答案 0 :(得分:0)

如果您的List有一个元素,我假设SomeClass.include(Module) first_都指向该元素。那么当List包含恰好一个元素时调用PopBack会发生什么:

last

也许你可以这样做:

template < typename T >
void List<T>::PopBack()
{
  if (Empty())  // Not empty so don't take the if
  {
    std::cerr << "** List error: PopBack() called on empty list\n";
    return;
  }

  Link *oldLink = last_;

  // first_ and last_ are the same so set them to 0
  if (first_ == last_)
    first_ = last_ = 0;  // Why 0 instead of nullptr ?

    // GetPred(0) will return nullptr, i.e. currLink  will be nullptr
    Link *currLink = GetPred(last_);

    // Last will be nullptr
    last_ = currLink;

    // Dereference of nullptr !!!!
    last_->next_ = nullptr;

    delete oldLink;

}

答案 1 :(得分:0)

方法PopBack在first_等于last_时具有未定义的行为,因为最初两个节点都设置为0

if (first_ == last_)
  first_ = last_ = 0;

然后您尝试访问数据成员next以获取等于0的节点

last_->next_ = nullptr;

该功能可以写得更简单

template < typename T >
void List<T>::PopBack()
{
    if ( Empty() )
    {
        std::cerr << "** List error: PopBack() called on empty list\n";
        return;
    }

    Link *oldLink = last_;
    Link *currLink = GetPred( last_ );

    last_ = currLink;

    if ( last_ == nullptr )
    {
        first_ = last_;
    }
    else
    {
        last_->next_ = nullptr;
    }

    delete oldLink;

} // end PopBack()

方法Insert我理解内存泄漏,因为首先分配一个节点,并将其地址分配给newLink

Link* newLink = NewLink(t);

然后重新分配此变量

newLink = currLink;

似乎这个方法实际上什么都没插入。

它可以看起来如下

// Insert t at (in front of) i; return i at new element
template < typename T >
ListIterator<T> List<T>::Insert (ListIterator<T> i, const T& t)
{
    if (Empty())  // always insert
    {
        i = End();
    }

    if ( !i.Valid() ) // null
    {
        std::cerr << " ** cannot insert at position -1\n";
        return End();
    }

    Link* newLink = NewLink(t);
    newLink->next = i.curr;

    Link* currLink = GetPred( i.curr_ );

    if ( currLink == nullptr )
    {
        first_ = newLink;
    }
    else
    {
        currLink->next = newLink;
    }   

    i.curr = newLink;

    return i;
}