Previously, we saw how we could use range-based for loops with any type that has appropriate begin
and end
methods, such as std::vector
and std::forward_list
.
It stands to reason, therefore, that we should be able to make our own types compatible with range-based for loops.
We may want to do something like this:
class Character {};
class Party {};
int main() {
Party MyParty;
for (const auto& Character : MyParty) {
// ...
}
}
Lets see how we can set this up.
In the next lesson, we will see an example where we create iterators entirely from scratch. But, in most cases, this is entirely unnecessary.
The most typical scenario is that our custom type has some functions and variables specific to our type, but is using a container for storage that already supports iterators.
This might be an array, vector, or linked list, for example.
A typical Party
class might look a little like this:
#include <vector>
class Character {};
class Party {
public:
// Party-specific logic
void AddMember() {};
void StartQuest() {};
void Disband() {};
void SetLeader() {};
private:
// Underlying Collection
std::vector<Character> PartyMembers;
};
To make our party iterable, we can just expose the begin
and end
methods of our underlying container. Note, our begin
and end
methods must be public
for this to work:
#include <vector>
class Character {};
class Party {
public:
// Party-specific logic
// ...
// Iterators
auto begin() {
return PartyMembers.begin();
}
auto end() {
return PartyMembers.end();
}
private:
// Underlying Collection
vector<Character> PartyMembers;
};
Above, the return type of our new functions was set to auto
. This is typical, but if we ever need to be specific about the type of an iterator from the standard library containers, there’s a static iterator
property that we can use:
vector<Character>::iterator begin() {
return PartyMembers.begin();
}
vector<Character>::iterator end() {
return PartyMembers.end();
}
With the public begin
and end
methods added to our class, the code in our original example will now work. This allows us to iterate over every character in our party using a range-based for loop:
int main() {
Party MyParty;
for (const auto& Character : MyParty) {
// ...do things
}
}
A complete example is available here:
#include <vector>
#include <iostream>
#include <utility>
using namespace std;
class Character {
public:
Character(string Name) : Name(std::move(Name)) {}
string GetName() const { return Name; }
private:
string Name;
};
class Party {
public:
void AddMember(const string& NewMember) {
PartyMembers.emplace_back(NewMember);
}
// Iterators
auto begin() {
return PartyMembers.begin();
}
auto end() {
return PartyMembers.end();
}
private:
vector<Character> PartyMembers;
};
int main() {
Party MyParty;
MyParty.AddMember("Legolas");
MyParty.AddMember("Gimli");
MyParty.AddMember("Frodo");
for (const auto& Character : MyParty) {
cout << Character.GetName() << endl;
}
}
Legolas
Gimli
Frodo
Updating our class to support iterators also means they can be used in other ways, which we’ll explore fully in the algorithm chapter later in this course.
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.