我已经用C ++创建了一个链表,并希望为其实现一个迭代器,以便可以进行范围循环:for (const int& i : list)
,其中Linked_List<int> list;
。
我的想法是像这样在Iterator
类中创建Linked_List
:
这是我到目前为止得到的:
template <typename T>
class Linked_List
{
public:
struct Iterator;
struct Node;
public:
Linked_List();
~Linked_List() noexcept(false);
Linked_List(const Linked_List&) = delete;
Linked_List(Linked_List&&) = delete;
Linked_List& operator=(const Linked_List&) = delete;
Linked_List& operator=(Linked_List&&) = delete;
void push_back(T);
void push_front(T);
void pop_back();
void pop_front();
bool empty() const;
T back() const;
T front() const;
//void swap(T, T);
//void insert(Iterator, T);
//void erase(Iterator);
//Iterator begin() const;
//Iterator end() const;
private:
Node* head;
Node* tail;
};
template<typename T>
struct Linked_List<T>::Node
{
Node() : prev(nullptr), next(nullptr) {}
Node(T t) : value(t), prev(nullptr), next(nullptr) {}
Node* prev;
Node* next;
T value;
};
current->next == tail
是否应该进行错误检查?如果是这样,我该怎么做?因为我的迭代器没有带尾的列表对象。 修改:
我不确定如何实现struct Iterator;
,在弄清楚如何将其与列表连接时会卡住,以便检查迭代器返回的当前节点是否等于列表中的尾部。 Linked_List Iterator end() const
方法。
假设我已经为这样的迭代器实现了所有必需的运算符:
struct Iterator
{
T& operator*() const { return current->value; }
bool operator!=(const Iterator& rhs) { return (*_current != rhs._current); }
Iterator& operator++()
{
current = current->next;
return *this;
}
};
我现在将如何实施Iterator Linked_List<T>::begin() const;
和end()
?
我想象一个虚构的用户制作这样的迭代器对象:
Linked_List<int>::Iterator it;
一个想法是有一个没有参数的公共构造函数,和一个将节点_current
设置为参数,并把Linked_List
类作为朋友的私有构造函数。 / p>
答案 0 :(得分:1)
一些笔记。
有两个用于声明#include <stdio.h>
int main(void) {
FILE *fp;
char s[] = "abcdef";
int i, c;
fp = fopen("ot.txt", "w+");
if (fp == NULL) {
printf("file open error\n");
return 1;
}
i = 0;
while (s[i] != '\0') {
fputc(s[i], fp);
i++;
printf("%d", i);
}
rewind(fp);
while ((c1 = fgetc(fp)) != EOF) {
putchar(c1);
}
printf("\n");
fclose(fp);
return 0;
}
和Node
的选项。在列表类内部为Iterator
,在外部类为List<T>::Node
。部分原因在于口味。但是从工程的角度来看,嵌套类的符号名更长,因此debuginfo更大。另外,如果嵌套类也是模板,则在必要时/很难对其进行专业化处理(因为首先需要完全对封闭模板进行专业化处理),但这不是这种情况。
当一个列表节点用作列表头和尾时,它会导致代码更优美。空列表是其Node<T>
和next
指向其自身的节点。 prev
附加到push_front
,后者指向第一个节点或本身。 list.next
将一个节点附加到push_back
,该节点指向最后一个节点或自身。插入/删除节点时,不需要对第一个节点和最后一个节点进行特殊处理。例如。 :
list.prev
在上面,struct Node {
Node *next_, *prev_;
Node()
: next_(this), prev_(this)
{}
~Node() {
unlink();
}
void push_back(Node* n) {
n->next_ = this;
n->prev_ = prev_;
prev_->next_ = n;
prev_ = n;
}
void unlink() {
Node *next = next_, *prev = prev_;
next->prev_ = prev;
prev->next_ = next;
next_ = this;
prev_ = this;
}
};
仅需要两项操作即可维护列表。除此之外,Node
本身就是一个极简列表,可用于侵入式列表(在析构函数中具有自动取消链接)。请注意,使用Node
是如何使对this
的检查变得不必要-nullptr
始终是有效列表。
错误检查应仅在调试模式下(例如,使用Node
)。否则,这些检查会通过不必要的运行时检查来惩罚正确的应用程序。
这是一个基于您的想法的最小工作示例:
assert