Implement iterator

Aug 13, 2020 at 3:42am
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 Aug 13, 2020 at 3:44am
Aug 13, 2020 at 4:01am
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 Aug 13, 2020 at 4:05am
Aug 13, 2020 at 4:41am
Nice, thank you
Aug 13, 2020 at 4:59am
one more question.
If I have array of pointers will it be the same? For example int **data
Aug 15, 2020 at 4:42pm
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 Aug 15, 2020 at 4:51pm
Aug 24, 2020 at 2:12pm
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;
     }
}
Aug 24, 2020 at 3:38pm
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.
Aug 28, 2020 at 12:28pm
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 Aug 28, 2020 at 12:49pm
Aug 28, 2020 at 1:50pm
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.
Aug 28, 2020 at 3:14pm
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.
Aug 28, 2020 at 3:38pm
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.