【问题标题】:Iterator in my implementation of std::list does not work我的 std::list 实现中的迭代器不起作用
【发布时间】:2016-08-11 15:37:34
【问题描述】:

我正在尝试编写一个列表模板,例如 std::list one。 这是我在 List.h 中的代码:

#include <memory>
#include <cassert>
#include <iterator>

template<typename T, class Node>
class iterator : public std::iterator<std::bidirectional_iterator_tag, Node *, Node &> {
    Node *underlying;

public:
    explicit iterator(Node *n) : underlying(n) { };

    iterator() : underlying(nullptr) { };

    iterator &operator++() { //preinc
    assert(underlying != nullptr && "Out-of-bounds iterator increment!");
    underlying = underlying->next;
    return *this;
    }

    iterator operator++(int) { //postinc
    assert(underlying != nullptr && "Out-of-bounds iterator increment!");
    iterator temp(*this);
    ++(*this);
    return temp;
    }

    iterator &operator--() { //predec
    assert(underlying != nullptr && "Out-of-bounds iterator decrement!");
    underlying = underlying->previous;
    return *this;
    }

    iterator operator--(int) { //postdec
    assert(underlying != nullptr && "Out-of-bounds iterator decrement!");
    iterator temp(*this);
    --(*this);
    return temp;
    }

    bool operator==(const iterator &rhs) {
    return underlying == rhs.underlying;

    }

    bool operator!=(const iterator &rhs) {
    return underlying != rhs.underlying;

    }

    T &operator*() {
    return underlying->data;
    }
};

template<typename T>
class List {

    class Node {
    public:
    T data;
    Node *previous;
    Node *next; //is that T needed?
    Node(T &d) : data(d) { };
    };

private:

    Node *head; //first element
    Node *tail;

    void create() { head = tail = NULL; }

    void create(const List &rhs) {
    iterator this_iter = head;
    iterator rhs_iter = rhs.head;
    while (rhs_iter != NULL) {
        this_iter->data = (rhs_iter++)->data;
        ++this_iter;
    }
    };

public:
    typedef T *iterator;
    typedef const T *const_iterator;
    typedef size_t size_type;
    typedef T value_type;

    List() { create(); };

    List &operator=(const List &rhs) {
    if (&rhs != this) {
        create(rhs);
    }
    return *this;
    };

    List(const List &rhs) { create(rhs); };

    ~List() { while(head) remove(head); };

    T *begin() { return head; };

    T *end() { return tail; };

    T front() { return head->data; };

    T back() { return tail->data; };

    bool empty() { return head == NULL; }

    size_type size() {
    size_t i = 0;
    Node *node = head;
    while (node) {
        node = node->next;
        i++;
    }
    return i;
    };

    T &operator[](size_type i) {
    if (i < size() && i >= 0) {
        Node *temp = head;
        while (i > 0) {
            temp = temp->next;
            i--;
        }
        return temp->data;
    }
    throw std::out_of_range("Index out of range");
    };

//    const T &operator[](size_type i) const; //how to implement and do not duplicate code?

    Node *push_back(value_type data) {
    Node *n = new Node(data);
    if (head == NULL) {
        head = tail = n;
    } else {
        n->previous = tail;
        tail->next = n;
        tail = n;
    }
    return n;
    };

    Node *push_front(value_type data) {
    Node *n = new Node(data);
    if (head == NULL) {
        head = tail = n;
    } else {
        n->next = head;
        head->previous = n;
        head = n;
    }
    return n;
    };

    void pop_front() {
    remove(head);
    };

    void pop_back() {
    remove(tail);
    };

    void remove(Node *n){
    if(n == NULL) return;
    if(n == head){
        head = n->next;
        head->previous =NULL;
    }
    else if(n == tail){
        tail = n->previous;
        tail->next = NULL;
    }
    else{
        n->previous->next = n->next;
        n->next->previous = n->previous;
    }
    delete n;
    }


};

这是 main.cpp

#include <iostream>
#include "List.h"
int main(){
    List<int> l;
    l.push_back(1);
    l.push_back(2);
    l.push_back(3);
    l.pop_back();
    l.pop_front();
    l.push_back(4);
    l.push_back(5);
    for (size_t i = 0; i < l.size(); i++)
        std::cout << l[i] << "\n";
    std::cout<<"Front "<<l.front();
    std::cout<<"Back "<<l.back();
}

实际上 push_back/front 、 pop_back/front 和 []operator 工作正常。但我得到“进程完成,退出代码 139” 当我尝试使用 front() 或 back() 时出错。而且我知道这个列表模板的迭代器不起作用,但我知道如何将它组合起来。有人可以提示或帮助吗?

编辑: 好的,我已经解决了删除和 front()、tail() 方法的问题。但是迭代器的东西仍然不起作用。 例如这段代码:

for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
        std::cout << it << "\n";
    }

给我错误:

error: cannot convert ‘List<int>::Node*’ to ‘List<int>::iterator {aka int*}’ in initialization
     for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
                                          ^
error: comparison between distinct pointer types ‘List<int>::iterator {aka int*}’ and ‘List<int>::Node*’ lacks a cast [-fpermissive]
     for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
                                                       ^

我知道问题在于用迭代器模板包装节点并且我有“typename T *iterator”。

【问题讨论】:

  • 您的Node(T &amp;d) 既没有设置next 也没有设置previous...可能不是什么大问题
  • 测试这段代码!创建空列表 - 检查它是否为空 - 调用其函数验证一切是否正常。使用一个元素创建列表 - 调用列表函数 - 验证一切正常。对两个元素 List 执行相同的操作。等等等等……
  • 然后,如果您仍然无法解决您的问题,您需要构建一个最小的程序来重现它,然后其他人可能能够提供帮助
  • 我已经测试过了。我添加了我的 main.cpp 文件

标签: c++ templates c++11 linked-list iterator


【解决方案1】:

您的 beginend 方法返回 Node*,而不是您的迭代器类型。您创建了接受Node* 作为参数explicit 的迭代器构造函数;您告诉编译器不允许从 List&lt;int&gt;::Node*List&lt;int&gt;::iterator 的隐式转换。

您必须执行以下操作之一:

  1. explicit iterator(Node *n) : underlying(n) { }; 中删除 explicit(尽管这可能会在您不想要的场景中进行隐式转换)
  2. beginend 更改为返回iterator(head)iterator(tail)(执行显式转换为iterator)而不是headtail(并更改begin 和@ 的返回类型987654342@ 到 iterator,无论如何你都应该这样做)

你还有一些其他问题:

  1. 你不应该在List中完成typedef T* iterator;这隐藏了您的 iterator 类的定义,因此 List 从未使用过它。修复该问题(并为您对 iterator 的使用添加必要的模板)使其编译
  2. iterator的继承定义应该是template&lt;typename T, class Node&gt; class iterator : public std::iterator&lt;std::bidirectional_iterator_tag, T&gt;而不是template&lt;typename T, class Node&gt; class iterator : public std::iterator&lt;std::bidirectional_iterator_tag, Node *, Node &amp;&gt;;后者声明解除引用的值应该是Node *(当你希望它是T时,每个Node中的值)
  3. end 应该返回 iterator&lt;T, Node&gt;(nullptr) 而不是 iterator(tail);否则,当您想在终止循环之前打印tail 时,您会在打印tail 的值之前停止。

完成所有这些后,您应该编译并获得您期望的结果。代码还是有问题,例如

  1. const 不正确,不提供各种访问器的const 版本,这会妨碍优化;如果编译器无法确定循环是实际上非变异
  2. ,那么您最终可能会在每个循环上重新计算size
  3. 复制构造函数/赋值实用程序函数create 不起作用(您想反复迭代rhspush_back,您不能使用iterator 推送新值李>
  4. 缺少const 访问器意味着无法使实用函数工作;它需要一个 const 迭代器类型来迭代 rhs 并保证它不会违反 const List&amp; 要求,但您只定义了变异迭代器函数

但它会在您未执行的代码中进行性能优化和正确性;一旦你对新代码感到满意,你就可以解决这个问题。

我有your code(尽管复制构造/赋值的修复是一种令人震惊的黑客攻击,使用const_cast 来蛮力解决缺乏const 安全迭代的问题);我还添加了一些测试来显示复制构造和分配工作。 Take a look.

【讨论】:

  • @Mateusz:您忘记将beginend 的返回类型更改为匹配。他们需要返回iterator,而不是Node*。修复编译和运行。
  • 嗯,还是同样的错误。 cpp.sh/2ln7 我假设你的意思是 *begin() 和 *end() 方法
  • @Mateusz:你离开了*。他们不应该返回iterator *,他们应该返回iterator。他们过去常常返回Node*,因为返回Node 的副本是荒谬的,但是对于iterator(围绕Node* 的薄包装),您希望按值返回,而不是按引用/指针返回。也就是说,我只是注意到您使用 typedef 替换了 iterator 来定义 List,因此您并没有使用您的 iterator 类。还有许多其他问题;我已经更新了答案以解决更多问题。
  • @Mateusz:现在 cpp.sh 似乎又可以工作了,我提供了一个 link to modified code 来解决所有这些问题(尽管它使用 const_cast hack 来解决缺乏const 友好的begin/end;我不想实现全新的代码)。
  • 好的,非常感谢。不幸的是,当我将此代码复制到我的 IDE 中时,我遇到了很多错误,例如: error: 'List::iterator' is not a template iterator begin() { return iterator(头); };
【解决方案2】:

问题(一个问题?)在remove():你不检查head是否是NULLhead case),如果tailNULLtail case)如果n-&gt;previousn-&gt;next 为空(一般情况)

我建议remove()

void remove(Node *n){
   if(n == NULL) return;
   if(n == head){
      head = n->next;
      if ( head )
         head->previous =NULL;
   }
   else if(n == tail){
      tail = n->previous;
      if ( tail )
         tail->next = NULL;
   }
   else{
      if ( n->previous )
         n->previous->next = n->next;
      if ( n->next )
         n->next->previous = n->previous;
   }
   delete n;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    相关资源
    最近更新 更多