Implement iterator

I read somewhere that it is possible to implement an `iterator` through defining begin/end methods, so object can be used in the for loop:

1
2
3
for(const auto &item:myCoolObject){
//do something
}


could somebody share example?
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
constexpr int sz = 3;

class my_array 
{
  int data[sz] { 1, 2, 3 }; 

public:
  int const* begin() const { return data; } 
  int const* end() const { return begin() + sz; }
} array;

#include <iostream>

int main()
{
  for (auto const& elt: array) 
    std::cout << elt << '\n';   
}


There are various other ways to define the meaning of range-based for loops over a particular object. See https://en.cppreference.com/w/cpp/language/range-for
Last edited on
Nice, thank you
one more question.
If I have array of pointers will it be the same? For example int **data
If I have array of pointers will it be the same? For example int **data

You'll need to adjust begin and end to return a type which works as an iterator. Which could be int* const* or int** for const and non-const my_array values respectively.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <utility>
#include <iostream>

class my_array 
{
  int **data = nullptr, size = 0;
  
public:
  ~my_array() { delete[] data; }
  
  my_array& operator=(my_array const&) = delete;
  my_array(my_array const&) = delete;
  
  my_array& operator=(my_array&& other) noexcept
  {
    size = std::exchange(other.size, 0);
    data = std::exchange(other.data, nullptr);
    return *this;
  }
  my_array(my_array&& other) noexcept
  {
    *this = std::move(other);
  }
  
  explicit my_array(int s)
    : data{new int*[s] {}}, size(s)
  {}

  int* const* begin() const { return data; } 
  int* const* end()   const { return begin() + size; }
  int**       begin()       { return data; }
  int**       end()         { return begin() + size; }
};

int main()
{
  for (auto const& elt: my_array(3)) 
    std::cout << elt << '\n';   
}
Last edited on
I've seen some pretty weird ways of iteration in C++, as someone coming from Python.

Like the one from w3schools.

1
2
3
4
string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"};
for(int i = 0; i < 4; i++) {
  cout << i << ": " << cars[i] << "\n";
}


It's a correct way obviously but they don't teach how iterate over something properly unlike this site (https://coderslegacy.com/c/c-strings/), which does.

1
2
3
4
5
6
int main() { 
     string str = "Hello";
     for(char&amp; c : str) {
         cout << c << endl;
     }
}
https://www.geeksforgeeks.org/introduction-iterators-c/

I like that site, it explains things in fairly simple and easy to understand ways, without a lot of nonsense.
Note that there is a problem with the element deletion example.

1
2
3
4
5
6
7
8
// Deleting a element using iterators 
    for (i = v.begin(); i != v.end(); ++i) { 
        if (i == v.begin() + 1) { 
            i = v.erase(i); 
            // i now points to the element after the 
            // deleted element 
        } 
    } 


As the comment correctly states, after the .erase() i points to the element after the deleted element - which is what is needed to be compared with .end() for correct termination. But i is incremented in the for loop so now points one element past the element following the deletion. It 'works' in this example as there are following entries. But if the deleted element was the last element then i will be one past .end() so the for comparison test will never succeed to give an infinite loop.

A 'better' solution could be:

1
2
3
4
5
6
// Deleting an element using iterators 
    for (auto i = v.begin(); i != v.end(); )
        if (i == v.begin() + 1)    // test for deletion of element
            i = v.erase(i);        // i now points to the element after the deleted element
        else
            ++i;


where i is only incremented if the deletion test fails.
Last edited on
v.erase(v.begin()+1);
your code would remove all elements except the first (not just an element), would be similar to v.erase(v.begin()+1, v.end()); provided that the vector does hold enough elements.

the examples are awful, it's quite idiotic to use a loop there.
True. I didn't study the original example carefully enough! I just saw that i was incremented after the .erase() which is usually not what is required - as the deletion test is usually on a value not a position. Ouch!!!

My suggestion would have been better for say removing all elements that had the value 2

1
2
3
4
5
6
// Deleting an element using iterators 
    for (auto i = v.begin(); i != v.end(); )
        if (*i == 2)                // test for deletion of element
            i = v.erase(i);         // i now points to the element after the deleted element
        else
            ++i;


I agree that the examples are awful and possibly hinder understanding rather than helping.
1
2
3
4
5
for (auto i = v.begin(); i != v.end(); )
        if (*i == 2)                // test for deletion of element
            i = v.erase(i);         // i now points to the element after the deleted element
        else
            ++i;
This code requires quadratic time and therefore represents a serious performance bug.

Instead, use
v.erase(std::remove(std::begin(v), std::end(v), 2), std::end(v));
Which requires linear time.

Topic archived. No new replies allowed.