I implemented a trivial class MyClass that has an array allocated with new inside it (I know that I could use a STL container, but I'm trying to understand how they work). I also created an iterator subclass, able to iterate on all the elements of a MyClass object:
class MyIterator : public iterator<forward_iterator_tag,int>
{
private:
int* data= nullptr;
int length;
int pointer=0;
int nullvalue=0;
public:
MyIterator(int* data, int length, bool end= false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
~MyIterator()
{
delete[] data;
}
MyIterator& operator++()
{
if(pointer!= length-1)
{
pointer++;
}
else
{
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other)
{
return pointer==other.pointer;
}
bool operator!=(const MyIterator& other)
{
return pointer!= other.pointer;
}
int& operator* ()
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
};
class MyClass
{
private:
int* data= nullptr;
int length= 100;
public:
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
iterator<forward_iterator_tag,int> begin()
{
return MyIterator(data,length);
}
iterator<forward_iterator_tag,int> end()
{
return MyIterator(data,length,true);
}
};
While the iterator works if I use it this way:
for(MyIterator i= MyClass_instance.begin(); i!=MyClass_instance.end();i++) {...}
It doesn't work if I try to use it with BOOST_FOREACH:
BOOST_FOREACH(int i, MyClass_instance) {...}
These are the errors that I get:
You're slicing your iterator by returning them as std::iterator<> by value. You cannot do that.
Returning by reference would avoid the slicing problem but introduces a worse problem: it returns a reference to a temporary¹.
Hence, fix it by returning your actual iterator type by value.
Your type was missing a const iterator.
All the iterator members weren't const-correct.
Also, according to the page Extensibility it looks like you need to add
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
There is serious trouble with the Rule-Of-Three implementation for your iterator type (What is The Rule of Three?).
You're deleting the container's data every time an iterator goes out of existence. And MyClass itself never frees the data...
Fixing most (?) of the above:
Live On Coliru
#include <iterator>
#include <boost/foreach.hpp>
class MyClass
{
private:
int* data = nullptr;
int length = 100;
public:
class MyIterator : public std::iterator<std::forward_iterator_tag, int>
{
public:
MyIterator(int* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyIterator& other) const { return pointer!= other.pointer; }
int& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
mutable value_type nullvalue = 0;
};
class MyConstIterator : public std::iterator<std::forward_iterator_tag, const int>
{
public:
MyConstIterator(int const* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyConstIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyConstIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyConstIterator& other) const { return pointer!= other.pointer; }
int const& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
value_type nullvalue = 0;
};
public:
typedef MyIterator iterator_type;
typedef MyConstIterator const_iterator_type;
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
~MyClass() {
delete[] data;
}
iterator_type begin() { return MyIterator(data,length); }
iterator_type end() { return MyIterator(data,length,true); }
const_iterator_type begin() const { return MyConstIterator(data,length); }
const_iterator_type end() const { return MyConstIterator(data,length,true); }
};
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
#include <iostream>
int main()
{
MyClass c;
BOOST_FOREACH(int i, c) {
std::cout << i << "\n";
}
}
¹ (unless you store the iterators somewhere else, but that would be a huge anti-pattern for many many reasons)
Related
I have created a class called ring it works like that:
ring<std::string> example(2);
examples.add("one");
examples.add("two");
examples.add("three"); // this element will replace the first element
also, I have tried to make it iterable but it crashes with an exception Access violation reading location
the exception location is at cout in the main for loop
the program works very well without the for loop
the ring class:
template <class input>
class ring {
public:
class iterator;
private:
input* data;
int size;
int pos = 0;
public:
ring(const int size) : size(size), data(NULL) { data = new input[size]; }
~ring() { delete[] data; }
void add(input in) {
if (pos == size)
pos = 0;
data[pos++] = in;
}
iterator begin() { return iterator(0, *this); }
iterator end() { return iterator(size - 1, *this); }
input& get(int i) { return data[i]; }
};
the iterator class:
template <class input>
class ring<input>::iterator {
private:
int m_pos;
ring m_ring;
public:
iterator(int pos, ring& aRing) : m_pos(pos), m_ring(aRing) {}
~iterator() {}
input& operator* () { return m_ring.get(m_pos); }
bool operator!= (const iterator& other) const { return m_pos != other.m_pos; }
iterator& operator++(int) {
m_pos++;
return *this;
}
iterator& operator++() {
m_pos++;
return *this;
}
};
main:
int main() {
ring<string> textring(3);
textring.add("one");
textring.add("two");
textring.add("three");
textring.add("four");
for (auto text : textring) {
cout << text << endl;
}
}
Your program crashes because class ring doesn't implement rule of three. You should provide all copy operations.
What is the reason of crash?
In destructor the same memory is deleted twice due to shallow copy. This copy is made by begin/end which returns iterator in which you make copy of ring.
Quick solution, is to store ring inside iterator as reference:
template <class input>
class ring<input>::iterator {
private:
int m_pos;
ring& m_ring;
Demo
I'm trying this little exercice from my c++ class and i've manage to do this from now.
It's not fully completed, but for now i'm facing the eternal problem of template of internal class.
I've seen a lot of different solution here on stack and on other websites, but still missing someting.
+ my goal at the end is also to understand the "why".
It's an Iterator internal class needed to iterate to my array. I've seen some exemple giving 2 differents typename for External and Iternal class and then using the typedef, but i'm not sure what's the best way to implement it.
As you can understand my Iterator class need to take the same kind of type as my Array class.
I for sure will need to change some function signatures and add some <\T> here and there, but for now i just need to avoid all the template traps.
using namespace std;
template <typename T>
class Iterator;
template<typename T>
class Array{
T* _data;
size_t _size;
Iterator* _start;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T* operator*(){
return content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o):_size(o._size), _data(o._data), _start(o._start){}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data[0]);
}
Iterator end(){
return Iterator(_data[_size-1]);
}
};
Can you please give me a clue or help me with this.
For more here is my basic main:
#include "Array.h"
int main() {
Array<string> array({"h","e","l","l","o"});
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << *i << endl;
return 0;
}
Thank you!
There is no template Iterator at the global scope, so this is wrong:
template <typename T>
class Iterator;
Also, Array<T>::Iterator isn't a template, it's just an inner class. You can simply forward-declare it inside the class like this:
template<typename T>
class Array {
public:
class Iterator;
Then there are some bugs in your code (e.g. end() should be 1 past the last element, you need to dereference the iterator twice and construct one from a pointer).
Here's a fixed version:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Array {
T* _data;
size_t _size;
public:
class Iterator;
private:
Iterator* _start;
public:
class Iterator {
T* content;
public:
explicit Iterator(T* value) :content(value) {}
Iterator& operator++() {
++content;
return *this;
}
T* operator*() {
return content;
}
bool operator ==(const Iterator& o)const {
if (content == o.content) {
return true;
}
return false;
}
bool operator !=(const Iterator& o)const {
return !(*this == o);
}
};
Array(const size_t s) :_size(s) {
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o) :_size(o._size), _data(o._data), _start(o._start) {}
Array(const std::initializer_list<T>& list) :_size(list.size()) {
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while (start != fin) {
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array() {
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o) {
if (this != &o) {
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index) {
if (index < _size) {
return *_data[index];
}
}
const size_t size()const {
return _size;
}
Iterator begin() {
return _start;
}
Iterator end() {
return Iterator(_data + _size);
}
};
int main() {
Array<string> array({ "h","e","l","l","o" });
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << **i << endl;
}
Thank you #rustyx for your help,
Wasn't far from it, but realy thank you.
I will post my corrected and working code here in case it can help others.
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
template<typename T>
class Array{
T* _data;
size_t _size;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T& operator*(){
return *content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
}
Array(const Array& o):_size(o._size){
_data = new T[_size];
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data);
}
Iterator end(){
return Iterator(_data + _size);
}
};
I've got this class:
template<typename T>
class Konten
{
enum { ssize = 100 };
T stack[ssize];
int top;
public:
Konten() : top(0) {}
void push(T i) {
assert(top < ssize); stack[top++] = i;
};
T pop() {
assert(top > 0); return stack[--top];
};
int rozmiar() { return top; };
class iterator {
Konten& s;
int index;
public:
iterator(Konten& is) : s(is), index(0) {};
iterator(Konten& is, bool) : s(is), index(s.top) {};
T operator++() { // Prefix
assert(index < s.top);
return s.stack[++index];
};
T operator++(int) { // Postfix
assert(index < s.top);
return s.stack[index++];
};
T& operator*() const { return s.stack[index]; };
iterator& operator=(const iterator& rv) {
s = rv.s;
index = rv.index;
return *this;
}
};
iterator begin() { return iterator(*this); };
iterator end() { return iterator(*this, true); };
friend class iterator;
};
And as you see it has another class inside it. I want to create an object of the iterator class this way:
Konten<double> pier;
iterator it1(pier);
But I keep on getting following error: "argument list for class template "iterator" is missing".
What am I doing wrong?
Your iterator needs to be declared as Konten<double>::iterator.
A custom iterator on a std::vector returns an object of the following class:
class chunk {
public:
int value_;
size_t fake_index_;
};
The iterator updates both the fake_index_ and value_.
After that I create a range class with the iterator. Then in a range base for loop over the range class, I only use the value_, but not fake_index_.
Does that means the compiler will optimize out the iterator's update of fake_index_ ?
Test Code (with all the iterator stuff...)
#include <vector>
#include <iterator>
class Iterator;
class Chunk {
public:
int value_;
int base_bit_index_;
int real_index_;
std::vector<int>& data_;
Chunk (std::vector<int>& data, size_t real_index)
: data_{data}
{
base_bit_index_ = real_index*sizeof(int);
real_index_ = real_index;
value_ = data[real_index_];
}
int value() { return value_; }
private:
void increment_index() {
++real_index_;
base_bit_index_ += sizeof(int);
value_ = data_[real_index_];
}
friend class Iterator;
};
class Iterator : public std::iterator<std::input_iterator_tag, int>
{
private:
Chunk chunk_;
public:
Iterator(Chunk chunk) :chunk_(chunk) {}
Iterator(const Iterator& mit) : chunk_(mit.chunk_) {}
Iterator& operator++() {
chunk_.increment_index();
return *this;
}
Iterator operator++(int) {Iterator tmp(*this); operator++(); return tmp;}
bool operator==(const Iterator& rhs)
{
return chunk_.real_index_== rhs.chunk_.real_index_;
}
bool operator!=(const Iterator& rhs)
{
return !(operator==(rhs));
}
Chunk& operator*() {return chunk_;}
};
class Range
{
private:
std::vector<int>& data_;
public:
Range(std::vector<int>& data)
: data_{data}
{
}
Iterator begin()
{
Chunk chunk(data_, 0);
Iterator tmp(chunk);
return tmp;
}
Iterator end()
{
Chunk chunk(data_, data_.size());
Iterator tmp(chunk);
return tmp;
}
};
int main()
{
std::vector<int> v1;
v1.resize(100);
std::vector<int> v2;
Range X(v1);
for (auto chunk : X) {
v2.push_back(chunk.real_index_);
}
}
gcc godbolt
Test Code (without all the iterator stuff)
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v2;
for (size_t i = 0; i < 100; ++i) {
v2.push_back(i);
// std::cout<<i<<"\n";
}
}
gcc godbolt
My first impression: Looks like the iterator sucks horribly anyway.
I use an API that comes with an iteration functionality using a void* handle.
void* handle = BrowseInit();
while (BrowseGetNext(handle))
{
// ...
int x = BrowseGetData(handle);
}
BrowseFree(handle);
How would I go about wrapping this into a C++11 iterator for use in range-based for-loops? Since the value of the handle doesn't actually change, I need some trickery in operator != ().
class Iterator
{
public:
friend class it;
class it
{
public:
it(Iterator* data) : _data(data) { }
bool operator != (const it& other) const
{
// what should I put in here?
}
const it& operator ++ ()
{
BrowseGetNext(_data->_handle);
}
int operator * () const
{
return BrowseGetData(_data->_handle);
}
private:
Iterator* _data;
};
Iterator() : _handle(BrowseInit()) { }
~Iterator()
{
BrowseFree(_handle);
}
it begin() const
{
return it(this);
}
it end() const
{
return it(nullptr);
}
private:
void* _handle;
};
This should work.
class Iterator
{
public:
friend class it;
class it
{
public:
// Constructor for begin().
it(Iterator* data) : _data(data), index_(0) { }
// Constructor for end().
it(Iterator* data, int) : _data(data), index_(-1) { }
bool operator != (const it& other) const
{
return !(*this == other);
}
bool operator == (const it& other) const
{
return ( this->_data == other._data && this->_index == rhs._index );
}
const it& operator ++ ()
{
// Increment the index if there's more data.
// Otherwise, set it to -1.
if ( BrowseGetNext(_data->_handle) )
{
++index_;
}
else
{
index_ = -1;
}
}
int operator * () const
{
return BrowseGetData(_data->_handle);
}
private:
Iterator* _data;
int _index;
};
Iterator() : _handle(BrowseInit()) { }
~Iterator()
{
BrowseFree(_handle);
}
it begin() const
{
return it(this);
}
it end() const
{
return it(this, 0);
}
private:
void* _handle;
};
Update: Setup iterator_traits for it
template <> struct std::iterator_traits<Iterator::it>
{
typedef int difference_type;
typedef int value_type;
typedef int* pointer;
typedef int& reference;
typedef std::forward_iterator_tag iterator_category;
};
Thanks for the suggestion, Yakk.
Update 2
Instead of specializing std::iterator for Iterator::it, derive Iterator::it from std::iterator.
class it : public std::iterator<std::forward_iterator_tag, int, int>
{
....
};