Это мое пространство имен LinkedList
namespace LinkedList {
template<class T>
class Node{
public:
Node(const T& data)
:data_(data), next_(nullptr) {}
T data_;
Node<T> *next_;
Node<T> *operator++(){
return next_;
}
};
template<class T>
class LinkedList{
public:
LinkedList()
: head_(nullptr), tail_(nullptr) {}
~LinkedList(){
Node<T> *curr = head_;
Node<T> *next;
while(curr != nullptr){
next = curr->next_;
delete curr;
curr = next;
}
}
void Append(const T& data) {
Node<T> *tmp = new Node<T>(data);
if (head_ == nullptr) {
head_ = tmp;
tail_ = tmp;
} else if (head_ == tail_){
head_->next_ = tmp;
tail_ = tmp;
} else {
tail_->next_ = tmp;
tail_ = tmp;
}
}
void Present(){
for(Node<T> *curr = head_; curr != nullptr; curr=curr->next_){
std::cout << curr->data_ << std::endl;
}
}
Node<T> *begin(){
return head_;
}
Node<T> *end(){
return nullptr;
}
private:
Node<T> *head_;
Node<T> *tail_;
};
}
Я читал Iterators и хотел, чтобы мой объект LinkedList был совместим с диапазоном на основе циклов for. Это то, что я получил, прочитав о циклах for на основе диапазона
for(auto x: list){ }
эквивалентно
for(; begin != end; ++begin) { //*begin};
Я думал, что был дерзким и мог пропустить пару шагов перегрузки оператора (!=, *), чтобы мой LinkedList работал с диапазоном, основанным на циклах for, запрограммировав мой итератор в мой класс Node ни по какой другой причине, кроме того, что мне так хотелось. Я только второй день изучаю С++, поэтому это может быть глупый вопрос, но я все еще немного смущен тем, почему это не работает:
LinkedList::LinkedList<int> list;
list.Append(3);
list.Append(5);
for(auto x: list) { //blah blah blah}
Я знаю, что приращение моего префикса является основной проблемой, когда цикл for выполняет ++__begin, я надеюсь, что он делает ___begin=_begin->next, но это не так. Почему это? На мой взгляд, перегрузка оператора работает следующим образом: любые переменные-члены, на которые я ссылаюсь в функции перегрузки, ссылаются на экземпляр, над которым я работаю. И когда я возвращаю ptr, он устанавливает любой экземпляр, над которым я работаю, на этот ptr. Я знаю, что мое понимание этого неверно, потому что это не работает, поэтому, пожалуйста, кто-нибудь объясните мне, как это на самом деле работает, лол.
@super понял, вы невероятно полезны, спасибо!
@super, чтобы было ясно, в функции перегрузки вы действительно ссылаетесь на экземпляр, над которым работаете? и поскольку вы не можете установить это = чему-то другому, это невозможно?
Да. Вам нужен отдельный объект, который просто указывает на экземпляр Node
, и когда вы делаете ++
на нем, вы модифицируете его, чтобы он указывал на следующий экземпляр Node
.
Возвращаемое значение фактически здесь не используется. Но именно так работают все итераторы в стандартной библиотеке, и почти любой, кто использует ваш код, будет ожидать, что ваш итератор будет делать то же самое.
Полезное чтение: Написание собственного контейнера STL. Возможно, вам все это не понадобится, но вам понадобится довольно много классов итераторов, реализованных для работы на основе диапазона for
.
Вы написали
template<class T>
Node<T>* Node<T>::operator++();
который будет вызываться как
Node<int> n;
Node<int> *p = ++n; // calls the pre-increment operator
Обратите внимание, что это сильно отличается от канонической формы
Node<T>& Node<T>::operator++();
который возвращает ссылку на увеличенный объект, который можно использовать в выражении.
Но есть еще одна проблема: перегрузка оператора работает
Когда оператор появляется в выражении и по крайней мере один из его операндов имеет тип класса или тип перечисления,...
Но Node<T>*
— это не тип класса. Это указатель, а указатели уже имеют свои собственные встроенные операторы. Их нельзя перегружать. Node<T>*::operator++()
нет.
Напишите итератор. Это не так уж плохо, и они действительно работают!
NB. Итераторы предназначены для обобщения указателей и имеют совместимый интерфейс. Вы можете использовать необработанные указатели, если ваш контейнер в основном представляет собой массив, потому что увеличение указателя уже является правильным способом перебора массива.
Только когда мы хотим предоставить доступ, подобный массиву или указателю, к несмежной структуре, нам приходится выполнять эту дополнительную работу.
Оооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо в этом больше смысла...
Есть 2 отдельных оператора приращения, пре-инкремент и пост-инкремент.
++begin
является предварительным приращением (++ идет перед переменной), и для итератора он должен изменять себя и возвращать себя. Это делает ваш план встраивания его в классNode
более или менее невозможным в этом сценарии.