0%
November 30, 2022

Iterator

C++

Repo

Iterator

By comparing the old and modern form of iterator we will understand which functions are required to be implemented.

Original form of Iterator
for (Vector<int>::Iterator it = values.begin();
	it != values.end();
	it++
) {
		std::cout << *it << std::endl;
}
Modern Simplified form of Iterator
for (int value : values) {
	std::cout << value << std::endl;
}
Defining Iterator Class

Therefore we need to

  • define an iterator object (which is to be instantiated inside Vector class) that has defined:

    • ++
    • * (dereference)
    • ==
    • !=
  • define methods begin() and start() to output Iterator.

Next, a specific classname cannot be used as a template parameter, but a generic class that is parametrized by a typename:

template<typename T>
class Vector {
	...
}

can as well be used as a template parameter as follow:

template<typename Vector>
class VectorIterator {
public:
	using ValueType = typename Vector::ValueType;
	using PointerType = ValueType*;
	using ReferenceType = ValueType&;

public:
	VectorIterator(PointerType ptr) : m_Ptr(ptr) {
	}
	// prefix ++
	VectorIterator& operator++() {
		m_Ptr++;
		return *this;
	}
	ReferenceType operator*() {
		return *m_Ptr;
	}
	bool operator ==(const VectorIterator& other) const {
		return m_Ptr == other.m_Ptr;
	}
	bool operator !=(const VectorIterator& other) const {
		return !(*this == other);
	}
private:
	PointerType m_Ptr;
};

Some important takeaway:

  • Here using are all used to give various alias to types.
  • The scoped-alias created by using can be accessed by VectorIterator::PointerType.
  • In order to let compiler distinguish between types and static members, we add typename keyword when defining ValueType, this Vector::ValueType will be added in the next section.

Expand our Vector Class that Returns Iterator

A lot of hard work has been done in the VectorIterator class above. In our original Vector class we just point out the additional code that bring VectorIterator into play.

template<typename T>
class Vector {
public:
	using ValueType = T;
	using Iterator = VectorIterator<Vector<T>>;
	...

and

public:
	Iterator begin() {
		return Iterator(m_Data);
	}

	Iterator end() {
		return Iterator(m_Data + m_Size);
	}

Test our Iterator in main()

Now we can test our iterator by:

int main(){
	Vector<Vector3> vectors;

	vectors.EmplaceBack(5);
	vectors.EmplaceBack();
	vectors.PushBack(Vector3(3, 3, 1));
	vectors.PushBack(new Vector3(1, 5, 5));
	vectors.EmplaceBack(5, 5, 1);
	vectors.EmplaceBack(6);
	vectors.EmplaceBack(7);
	vectors.EmplaceBack(5, 5, 2);
	vectors.EmplaceBack(2, 2, 1);
	vectors.PopBack();
	vectors.PopBack();
	//PrintVector(vectors);

	for (Vector3& vector : vectors) {
		std::cout << vector << std::endl;
	}
}

and the output is the same as before:

EmplaceBack
EmplaceBack
Move
Move
Destroy
Destroy
Move
Destroy
Move
Move
Move
Destroy
Destroy
Destroy
Copy
Move
Destroy
EmplaceBack
Move
Move
Move
Move
Destroy
Destroy
Destroy
Destroy
EmplaceBack
EmplaceBack
Move
Move
Move
Move
Move
Move
Destroy
Destroy
Destroy
Destroy
Destroy
Destroy
EmplaceBack
EmplaceBack
Destroy
Destroy
(5, 5, 5)
(0, 0, 0)
(3, 3, 1)
(1, 5, 5)
(5, 5, 1)
(6, 6, 6)
(7, 7, 7)
Destroy
Destroy
Destroy
Destroy
Destroy
Destroy
Destroy