Implementing the C++ Container List Iterator Class Template
Writing a C++ Container - Part 5
Forward: In this part of the series, I explain how to implement the C++ Container List Iterator Class Template.
By: Chrysanthus Date Published: 11 Oct 2012
Introduction
Content of Implementation Code
Implementing the synopsis of a class usually concerns the definitions of the member functions of the class. The data members are usually found in the synopsis and not in the implementation. The implementation code has no start tag and no end tag, however, it is advisable to type all the member function definitions in one portion of the page (file).
In the implementation, you type a member function beginning with the class template tag (not the implementation tag - there is no implementation tag). Next you have the function return type (at the next line). If the return type is a class template, then it needs the angle brackets with the placeholder (generic type). Following, you have the class name or template name with angle brackets and placeholder. Then you have the scope resolution operator. You then have the name of the function and its parameters in parentheses. If a parameter is a class template, then it has the angle brackets with the placeholder. After the parentheses, you have the block of the function.
Operators are similarly defined. A single operand operator means right operand. An increment or decrement operator that is prefix has an empty parentheses; the one that is postfix has a parentheses with a type. However, the body of the member operator function is what really determines prefix or postfix.
The Iterator Default Constructor
The name of the iterator is TempIterator, for Template Iterator. It is a class template. The definition is:
//default iterator constructor
template<class T>
TempIterator<T>::TempIterator()
{
ThisList = 0;
ElementPtr = 0;
}
The iterator has two data members, which are ThisList and ElementPtr. They are both pointers: ThisList is a pointer to the list associated with the iterator; ElementPtr is a pointer to a particular element in the list. This constructor initializes them to zero each (null pointers). This means, this constructor does not associate them and so does not associates the iterator to any list (and any of its element). In this case, the association has to be done after the instantiation of the iterator.
The constructor that does association with a list during instantiation is:
template<class T>
TempIterator<T>::TempIterator(TempList<T> &L)
{
ThisList = &L;
ElementPtr = L.Front;
}
It has as parameter a reference to the list. The list should have been instantiated. The first statement in the function body points the data member, ThisList to the list. The second statement points ElementPtr to the first element of the list. And so the association is done. If you have forgotten how to use references then click the link below, titled, "C++ Course" to read the relevant tutorials.
Iterator Constructor for Particular List and Particular Element
There is a constructor that will point to a particular list and a particular element in the list. The element is not necessarilly the first element of the list. The constructor definition is:
template<class T>
TempIterator<T>::TempIterator(TempList<T> *p, Element<T> *q)
{
ThisList = p;
ElementPtr = q;
}
The parameters are a pointer to the list and a pointer to the particular element in the list. The first statement in the function body points ThisList to the list and the second statement points ElementPtr to an element in the list. If you have forgotten how to use pointers, then click the link below, titled, "C++ Course" to read the relevant tutorials.
Overloaded Increment Prefix Operator
This operator effectively points the iterator to the next element in the list and then returns the reference identifier iterator of the newly pointed element. The operator's return type is a reference of the iterator. The operator definition is :
template<class T>
TempIterator<T>& TempIterator<T>::operator++()
{
if (ElementPtr == 0)
{
cout << "End of list or list is empty." << '\n';
}
else
{
//Let iterator pointer point to the next element
ElementPtr = ElementPtr->Successor;
//return a forced reference of the iterator in its new state
return *this;
}
}
If the list is empty or if the iterator is pointing just to the end of the list (sentinel), the value of ElementPtr, which actually holds the pointer to the element would be zero (null). The body of the function first checks if that is the case. If it is not, it goes on to point the iterator's ElementPtr to the next element in the list.
"*this" here, actually means, the value of the iterator. It is equivalent to a reference identifier. It is a forced reference identifier. It references the newly pointed element. It is returned. The statement in the function body that points ElementPtr to the next element is what actually does the increment.
The definition is:
template<class T>
TempIterator<T> TempIterator<T>::operator++(int)
{
if (ElementPtr == 0)
{
cout << "End of list or list is empty." << '\n';
}
else
{
//remember the iterator with the current state of ElementPtr
TempIterator<T> remember = *this;
//Let iterator pointer point to the next element
ElementPtr = ElementPtr->Successor;
//return iterator state before iterator pointer was changed
return remember;
}
}
The definition is similar to the prefix case. The main difference is in the else part of the if-construct. That is where the postfix feature is implemented. The else part begins by assigning the forced reference identifier of the element the iterator is currently pointed to, to the identifier, remember. Then it points the iterator ElementPtr pointer to the next element. It then returns the value of remember. This is postfix in the sense that the state of the iterator returned, is one that is pointing to the element before increment.
Overloaded Decrement Prefix Operator
The definition is:
template<class T>
TempIterator<T>& TempIterator<T>::operator--()
{
if (ElementPtr == 0)
{
cout << "In front of list or list is empty." << '\n';
}
else
{
//Let iterator pointer point to the previous element
ElementPtr = ElementPtr->Predecessor;
//return a forced reference of the iterator in its new state
return *this;
}
}
The explanation is similar to the Overloaded Increment Prefix Operator, except for the fact that the coding works for the reverse direction. Read the code.
Overloaded Decrement Postfix Operator
The definition is:
template<class T>
TempIterator<T> TempIterator<T>::operator--(int)
{
if (ElementPtr == 0)
{
cout << "In front of list or list is empty." << '\n';
}
else
{
//remember the iterator with the current state of ElementPtr
TempIterator<T> remember = *this;
//Let iterator pointer point to the previous element
ElementPtr = ElementPtr->Predecessor;
//return iterator state before iterator pointer was changed
return remember;
}
}
The explanation is similar to the Overloaded Increment Postfix Operator, except for the fact that the coding works for the reverse direction. Read the code.
Note: the ++ and -- operators should be used only when the iterator is pointing to an element.
To obtain the value the iterator is pointing to, the * operator is overloaded. Remember, in an iterator, it is actually the ElementPtr (pointer to instantiated class) data member, ElementValue that is a pointer and points to the element in the list that has the value. Each element has a data member that holds the value. So from the iterator, you reach the element through ElementPtr. From the element, you obtain the value from ElementValue. The definition is:
template<class T>
T& TempIterator<T>::operator*()
{
if (ElementPtr == 0)
{
cout << "No element is pointed to." << '\n';
}
else
{
return ElementPtr->ElementValue;
}
}
The return statement with its expression, returns the value. If you do not know how to use the standard operator of -> for pointer to object, then click the "C++ Course" link below and read the relevant tutorial.
Overloaded Equality Operator
The overloaded equality operator test if two iterators are equal. It simply tests if the pointers of the two iterators have the same address. It has one operand (parameter), which is the right operand. It returns a bool (true or false). The definition is:
template<class T>
bool TempIterator<T>::operator==(const TempIterator<T> &p) const
{
return ElementPtr == p.ElementPtr;
}
The expression,
ElementPtr == p.ElementPtr
results in true if the addresses of the two pointers are equal or false if they are different. The result is returned. Here, == is the standard equality operator.
Overloaded Inequality Operator
This is the opposite of the above. However, here, all the data members of the two iterators are tested. The definition is:
template<class T>
bool TempIterator<T>::operator!=(const TempIterator<T> &p) const
{
return !(*this == p);
}
Here, "this" is a pointer to the left operand. It is the pointer to the iterator object that has the operator. "*this" means the value (content) of the object that "this" points to. The operator (function) body uses the standard ! and == operators.
You need a special type of operator to determine if an iterator is currently pointing to an element in the list. The definition is:
template<class T>
TempIterator<T>::operator bool () const
{
return *this != this->ThisList->end();
}
This iterator returns true or false and it is typed only as the iterator name, when in use (see later). The operator is called a cast operator. In the expression,
this->ThisList->end()
you have the operator -> twice. The left operand of the -> operator is a pointer to a class object. The right operand is a member of the object. The operator operates from left to right. The end() function of the list returns a new iterator that points to the sentinel (just beyond the last element). In the expression, "this->ThisList" returns the list that is associated with the current iterator, while "ThisList->end()" returns a new iterator that is pointing to the sentinel of the list. So the total expression returns a new iterator that points to the sentinel of the list associated with the current iterator.
In the return statement above, the new iterator is compared whether it is not equal to the current iterator (*this). A true or false value is returned. A value of false will be returned when the current active iterator is pointing to the sentinel.
Such an operator is used in the condition of a loop (see later).
You will find the word, Inspector in C++ documents from time to time. It means a function or operator whose body can only read (and use) the value of an identifier and not change it. Such a function or operator has the specifier, const, at the end of its prototype. The identifer and inspector are of the same class.
A Mutator
A mutator is a function or operator that can change the value of an identifier in its function body. The identifier and function (or operator) are of the same class (also friend class). Many mutators are also inspectors. Such a function or operator does not have "const" at the end of its prototype.
That is it for this part of the series. We stop here and continue in the next part.
Chrys
Related Courses
C++ CourseRelational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
NEXT