Reputation: 3
I made a doubly linked list. In my main, I need to use an external List Iterator that has a constructor that takes a list. This is what I have so far:
struct ListIterator : List {
Node* cur = head;
ListIterator(List* list) {
this -> list = list;
}
bool hasNext() {
return (cur -> next) != nullptr;
}
int next() {
return list.at(cur -> next);
}
};
This is my main:
List list1;
list1.append('I');
list1.append('D');
list1.append('F');
list1.append('G');
ListIterator it(list1);
while (it.hasNext()) {
cout << it.next();
}
As you can see, I'm trying to use hasNext() and next() to display all the nodes in a list. I'm confused on how to create a constructor that takes a list and use the function in ListIterator struct. Any tips and hints would be very much appreciated.
Upvotes: 0
Views: 667
Reputation: 2423
You seem to be heavily influenced by Java (or perhaps even C#). It's not a bad thing, you just need to learn the ins and outs of C++.
For your first problem: in C++, there is a clear distinction between values, references and pointers. When you declare an instance of your List
class as follows:
List list1;
that is actually a value. The instance exists on the stack and will be properly disposed off (the destructor will be called) once the scope is ended.
If you now were to initialize a second list as follows:
List list2 = list1;
that is also a value, and it will copy the entire list (the copy constructor or assignment operator will be called). Now list1
and list2
are two distinct list instances and modifying one will not affect the other.
Anyhow, to get a pointer to a list, you need the following syntax:
List *listPtr1 = &list1;
As it is a pointer, an indirection, copying it will not copy the underlying structure:
List *listPtr2 = listPtr1; // Will also point to list1
While using the proper syntax will solve your immediate problem, it doesn't properly address the odd iterator implementation.
In C++, iterators either point to a single element in a container or they point past-the-end. An iterator typically does not know if the end of the container is reached by itself; instead, it needs to be compared to this past-the-end iterator.
Containers typically define a begin
method that returns an iterator to the first element, and an end
method that returns an iterator to past-the-end.
Iterating over a container typically happens using the following prototype:
for (auto it = container.begin(); it != container.end(); ++it)
{
// To access the element, you need to dereference the iterator:
std::cout << "The current value is :" << *it << std::endl;
}
In this regard, iterators behave much like pointers to the elements. There's no need for the clunky next
and hasNext
methods. You simply get an iterator to the start of your container, make sure to stop iterating once it points to past-the-end, and increment it when you want to go to the next element.
A container that properly defines the begin
and end
methods can also be used in a range-based for loop:
for (auto &element : container)
{
std::cout << "The current value is :" << element << std::endl;
}
I understand it is fun and very educational to implement your own containers. I reinvented my fair share of wheels just to understand how things work exactly, so nothing wrong with that. But I would advice you to perhaps play around with standard C++ containers first, get the hang of the major differences between C++ and languages like Java and C# and then have another go at the exercise.
Upvotes: 1