C++: copy constructor for iterators - c++

I am currently in the process of refreshing my knowledge of the language, and have found the following example here defining an iterator class. Am I mistaken in saying that the way the copy constructor in this iterator is defined in such a way that it will only create a shallow copy or am I missing something?
Wouldn't this create problems when used given that the class attribute is an int pointer?
Thanks.
// std::iterator example
#include <iostream> // std::cout
#include <iterator> // std::iterator, std::input_iterator_tag
class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() {++p;return *this;}
MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
bool operator==(const MyIterator& rhs) const {return p==rhs.p;}
bool operator!=(const MyIterator& rhs) const {return p!=rhs.p;}
int& operator*() {return *p;}
};

The concept of "deep" copying only applies to objects owning another object through some kind of reference (e.g. a pointer). The iterator uses its value (the pointer) in an non-owning fashion, and so copying the value of that pointer is enough to maintain the semantics of the class.
In fact (as pointed by Caleth in the comments), in this case the compiler would generate precisely this copy-ctor by default, so you don't even have to write it.
If your copy-ctor actually copied the data being pointed to, it wouldn't be a very good iterator, because you'd then lose the access to the object you wanted to iterate.

Related

what the "range-based for" call of iteration

I am recently trying to know how ranged-for work,
and finally got a picture as below:
for(obj& temp_ref:range r)//hide iterator somewhere as the range defined.
For each iteration,there is just a update of current object to doing the job in one circle of loop,and this is decent explain for me.
 
 
 
But when I trying to combine range and iterator into a mixed class,
there`s a problem of which operator++ should it call in my code,
in the sense, it should be the derivative class method be call in range-based-for,
because I create the object as derivative object,my declare is virtual.
the code is provided here,the first part is sprated and fine,
but the second one is works weird for me.
#include <iostream>
namespace first
{
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
class jump2:public iterator<int>
{
public:
jump2(int* ini):iterator<int>(ini){};
virtual void operator++ (){i+=2;};
};
}
namespace second
{
template<class T>
class iterator
{
public:
iterator(T* inStart,T* inFinal):current(inStart),final(inFinal){};
T* current;
T* final;
iterator<T> begin(){return iterator<T>(this->current,this->final);};
iterator<T> end(){return iterator<T>(this->final,this->final);};
virtual iterator<T>& operator= (const iterator<T>& other){this->current=other.current;this->final=other.final;return *this;};
virtual T& operator* (){return *this->current;};
virtual bool operator!= (iterator<T>& other){return this->current!=other.final;};
virtual void operator++ ()
{
std::cout<<"<call base>";
this->current=this->current+1;
};
};
template<class T>
class jumper:public iterator<T>
{
public:
jumper(T* inStart,T* inFinal):iterator<T>(inStart,inFinal){};
void operator++ ()
{
std::cout<<"<call deri>";
this->current=this->current+2;
};
};
};
int main()
{
int a[6]={1,0,2,0,3,0};
//success
{
using namespace first;
range<jump2> a_range(jump2(a),jump2(a+6));
for(int store:a_range)
std::cout<<store<<std::endl;
}//pause();
//Fail
{
using namespace second;
jumper<int> a_iterator_call_jumper(a,a+6);
for(int& i:a_iterator_call_jumper)
std::cout<<i<<std::endl;
}//pause();
return 0;
};
this output is
1
2
3
1
<call base>0
<call base>2
<call base>0
<call base>3
<call base>0
<call base>
but it should be like
1
2
3
1
<call deri>2
<call deri>3
<call deri>
Is this thing goes wrong because I use it wrong?
Or there`s a bug I am not find yet?
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
no
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator begin() const {return a;};
iterator end() const {return b;};
};
yes.
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
no
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
iterator( iterator const& ) = default;
iterator& operator= (const iterator<T>& other) & = default;
T& operator*() {return *i;};
bool operator!= (const iterator& other) const {return this->i==other.i?0:1;};
void operator++() {++i;};
};
yes.
Don't use the standard C++ vtable-based polymorphic object model for iterators; they should be regular types, and the vtable-based polymorphic object model is not regular.
As for second, it is not a good plan. An iterator and the range over which you iterate are not the same thing. Smashing them together makes your class incoherent.
C++ iteration is not designed for dynamic dispatch (polymorphism). You can do it, but it kills performance, and it isn't trivial to write. You basically need to hide the virtual dispatch behind a regular type (and you can dispatch using vtables, or you can dispatch using C++'s void pointer based type erasure).
If you are used to other languages, many other languages descended from C++ use mandatory reference semantics with garbage collection for objects of class type. Their virtual inheritance object model works on reference variables, as it does in C++ with reference and pointer types.
C++ is relatively quirky (in this day and age) in that it supports complex objects as value types, and that the language and standard library presumed you are working with value types most of the time. (Note that a pointer is a kind of value type; but its value is the pointer, not the object), and that when you want to use reference/pointer semantics you have to adapt your code.

How can I define a STL compatible input iterator without interhitance?

I am trying to learn C++ and would like to understand iterators better. To that end, I tried to write an STL compatible iterator for a custom class (called StrBlob which holds strings, defined in the wonderful C++ Primer). Eventually, the following iterator definition worked:
class StrBlob::iterator
: public std::iterator<std::input_iterator_tag, // iterator_category
std::string, // value_type
std::string, // difference_type
const std::string*, // pointer
std::string // reference
> {
public:
iterator() : curr(0) {}
iterator(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
iterator& operator++(); // prefix operators
iterator& operator--();
iterator operator++(int); // postfix operators
iterator operator--(int);
std::string& operator*() const;
std::string* operator->() const;
bool operator==(const iterator& b) const { return curr == b.curr; }
bool operator!=(const iterator& b) const { return curr != b.curr; }
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t,
const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
Initially I tried for a long time in vain to design the iterator with the right type definitions myself. This didn't work, so I simply provided an inheritance to std::iterator as mentioned in cppreference.
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
(I tried to understand Stackoverflow answers to related questions about iterator design but they were a bit to abstract for me.)
Your code is bugged, std::string is not a suitable difference type (difference_type is the difference between two iterators so normally its std::ptrdiff_t), nor is std::string a suitable reference type (should be std::string&).
Here's the correct typedefs, you can use these instead of inheritance.
typedef std::input_iterator_tag iterator_category;
typedef std::string value_type;
typedef std::ptrdiff_t difference_type;
typedef std::string* pointer;
typedef std::string& reference;
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
Yes, std::iterator (deprecated in C++17), only provides typedef.
So you can add those typedef yourself instead of using inheritance.
class StrBlob::iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = std::string;
using difference_type = std::string;// should be `std::ptrdiff_t`
using pointer = const std::string*; // const should probably be dropped, unless you do const_iterator
using reference = std::string; // should be std::string&
// ...
};

How can I use a overloaded const_iterator inside a class?

I'm doing a University project in C++, whose purpose is to learn how to use the diferent STL containers and their iterators.
In my program, I have a class with a set:
class ConjuntoDeLetras{
private:
set<Letra> letras;
public:
ConjuntoDeLetras();
···
};
Inside the class, I have two nested class, iterator and const_iterator. (I don't know if it's the best way to make class iterators, but the teacher tell us that we need to do at this way):
class iterator{
private:
set<Letra>::iterator it;
public:
iterator();
Letra operator*();
ConjuntoDeLetras::iterator& operator++();
ConjuntoDeLetras::iterator& operator+=(int num);
bool operator==(const ConjuntoDeLetras::iterator &i);
bool operator!=(const ConjuntoDeLetras::iterator &i);
friend class ConjuntoDeLetras;
};
class const_iterator{
private:
set<Letra>::const_iterator it;
public:
const_iterator();
Letra operator*();
ConjuntoDeLetras::const_iterator& operator++();
ConjuntoDeLetras::const_iterator& operator+=(int num);
bool operator==(const ConjuntoDeLetras::const_iterator &i);
bool operator!=(const ConjuntoDeLetras::const_iterator &i);
friend class ConjuntoDeLetras;
};
Both iterator class methods works well. In ConjuntoDeLetras class we have the begin and end methods:
ConjuntoDeLetras::iterator begin();
ConjuntoDeLetras::const_iterator begin() const;
ConjuntoDeLetras::iterator end();
ConjuntoDeLetras::const_iterator end() const;
The problem is here. When I'm going to use the const_iterator we have problems:
ConjuntoDeLetras::const_iterator itL;
for(itL=L.begin(); itL!=L.end(); ++itL){
CantidadLetras aux;
aux.frecuenciaAbsoluta = 0;
aux.frecuenciaRelativa = 0;
aux.letra = (*itL).getLetra();
salida.push_back(aux);
}
When I execute this code the compiler says that I don't have operator= for const_iterator to iterator. I know the reason of the problem, it's because the object L is a not const variable and uses the normal iterator begin() and end(). I thought about removing the final const of the functions but I can't overload the function only with the return type. I don't know what is the best solution. Here is the compiler error:
error: no match for ‘operator=’ (operand types are ‘ConjuntoDeLetras::const_iterator’ and ‘ConjuntoDeLetras::iterator’)
for(itL=L.begin(); itL!=L.end(); ++itL){
^
And the same error with the end().
You are missing the other ++ operator and the required typedefs to be an iterator.
Plus, you need =default copy/move/assignments.
Finally you need a converting constructor from your iterator to your const_iterator.
Optionaly add two const_iterator::operator=(iterator) overloads (copy assign and move assign), plus const_iterator::operator==(iterator const&) and iterator==const_iterator similarly.

C++, having issues using const_iterator to make operator=

There is a compilation error which occurs when I write the following:
const_iterator it = cp.begin();
const_iterator is my own class for const iterator.
cp is an object of a class ConjuntoPreguntas (see below).
The error is:
mainprueba.cpp:30:6: error: no match for ‘operator=’ (operand types are ‘ConjuntoPreguntas::const_iterator’ and ‘ConjuntoPreguntas::iterator’)
cit = CP.begin();
^
mainprueba.cpp:30:6: note: candidate is:
In file included from mainprueba.cpp:2:0:
conjuntopreguntas.h:258:21: note: ConjuntoPreguntas::const_iterator& ConjuntoPreguntas::const_iterator::operator=(const ConjuntoPreguntas::const_iterator&)
const_iterator& operator=(const const_iterator& cit){
^
conjuntopreguntas.h:258:21: note: no known conversion for argument 1 from ‘ConjuntoPreguntas::iterator’ to ‘const ConjuntoPreguntas::const_iterator&’
The code:
class ConjuntoPreguntas{
private:
map<int,Pregunta> preguntas;
public:
class const_iterator;
class iterator{
private:
map<int,Pregunta>::iterator it;
public:
iterator & operator++(){
++it;
}
iterator & operator--(){
--it;
}
pair<const int,Pregunta> &operator *(){
return *it;
}
bool operator ==(const iterator &i){
return i.it==it;
}
bool operator !=(const iterator &i){
return i.it!=it;
}
friend class ConjuntoPreguntas;
friend class const_iterator;
};
class const_iterator{
private:
map<int,Pregunta>::iterator it;
public:
const_iterator(){
}
const_iterator & operator++(){
++it;
}
const_iterator & operator--(){
--it;
}
pair<const int,Pregunta> &operator *(){
return *it;
}
bool operator ==(const const_iterator &i){
return i.it==it;
}
bool operator !=(const const_iterator &i){
return i.it!=it;
}
const_iterator& operator=(const const_iterator& cit){
}
friend class ConjuntoPreguntas;
};
iterator begin(){
iterator i;
i.it=preguntas.begin();
return i;
}
iterator end(){
iterator i;
i.it=preguntas.end();
return i;
}
/* other code, irrelevant to the problem */
};
If anyone can help me, I will be very grateful.
Your most immediate problem is because you don't have a const version of begin:
const_iterator begin() const
{
const_iterator i;
i.it = preguntas.begin();
return i;
}
But then also your const_iterator class uses map's iterator which is going to result in another problem.
If you're writing a container-looking class, there's nothing for it but to write const-correct iterator and const_iterator classes and provide the const-correct members.
If however you are NOT writing a container, you may not want to do this. The best case is to provide a container-agnostic interface to the class, where for example you provide meaningful names rather than direct container access. Alternately provide const-only access via the map const_iterator (don't write your own iterator class).
If you want to fully encapsulate std container inside your class (daunting and usually unneccessary task) you need to make sure you also define all conversions. In particlar, standard container iterators have a constructor which creates const_iterator from iterator (but not the other way around!). You will have to create it yourselfa as well.
However, a much better design choice would be to simply expose the member map variable.

Container of shared_ptr's but iterate with raw pointers

I have a class that holds a list containing boost::shared_ptrs to objects of another class.
The class member functions that give access to the elemets in the list return raw pointers. For consistency I'd also like to be able to iterate with raw pointers instead of shared_ptrs. So when I dereference the list iterator, I'd like to get raw pointer, not a shared_ptr.
I assume I need to write a custom iterator for this. Is this correct? If so can someone point me in the right direction - I've never done this before.
Here's an option using Boost transform_iterator:
#include <list>
#include <boost/iterator/transform_iterator.hpp>
#include <tr1/memory>
#include <tr1/functional>
using std::list;
using std::tr1::shared_ptr;
using boost::transform_iterator;
using boost::make_transform_iterator;
using std::tr1::mem_fn;
using std::tr1::function;
struct Foo {};
struct Bar
{
typedef shared_ptr< Foo > Ptr;
typedef list< Ptr > List;
typedef function< Foo* (Ptr) > Functor;
typedef transform_iterator< Functor, List::iterator > Iterator;
Iterator begin()
{
return make_transform_iterator( fooptrs.begin(), mem_fn( &Ptr::get ) );
}
Iterator end()
{
return make_transform_iterator( fooptrs.end(), mem_fn( &Ptr::get ) );
}
List fooptrs;
};
C++11 would make it easy to eliminate the function wrapper but I don't have a compiler handy to test it out. You could also hide the concrete type of Iterator using type-erasure if you saw the need (I think Adobe offers a free any_iterator class template for this purpose.)
I sometimes see people reaching for STL containers of boost::shared_ptr when actually the less obvious and relatively little known boost::ptr_container might be a better choice.
This may or may not be one of those cases, but consider that one of the nice properties of the ptr_container classes is that their iterators have an "extra" indirection which helps keep things clean and safe.
🧟‍♀️ ... zombie thread ... 🧟
If you can't use Boost (e.g., it's part of your public interface and you don't want to make that a requirement for your users), you can roll your own relatively easily. For a container my_container -- which could be a front for a standard, boost, whatever container but whose actual implementation is hidden in your implementation files and not exposed to your API -- holding any smart pointer to a class my_type, it would be something like:
class iterator
{
public:
~iterator();
iterator( const iterator& other );
iterator& operator=( const iterator& other );
// The standard iterator traits (names must be lower-case)
typedef std::forward_iterator_tag iterator_category;
typedef my_type* value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type reference;
iterator& operator++();
iterator& operator++( int );
reference operator*() const;
friend bool operator==( const iterator& it1, const iterator& it2 );
friend bool operator!=( const iterator& it1, const iterator& it2 );
private:
// Private, type-erased construction
friend class my_container;
explicit iterator( void* );
// Implementation hidden by pimpl
struct impl;
impl* _impl;
};
And in the cpp file, assuming we're using a vector of shared_ptr under the hood:
// Define the Pimpl struct
struct iterator::impl
{
typedef std::vector< std::shared_ptr<my_type> >::iterator iterator;
iterator iter;
};
// Use void* as type erasure to hide the actual types from the user
iterator::iterator( void* wrappedIter )
: _impl( new impl( *reinterpret_cast<impl::iterator*>( wrappedIter ) ) )
{
}
// Copying
iterator::iterator( const iterator& other )
: _impl( new impl( *other._impl ) )
{}
iterator& iterator::operator=( const iterator& other )
{
_impl->iter = other._impl->iter;
return *this;
}
// ... could implement moving too ...
iterator::~iterator()
{
// Destroy the pimpl
delete _impl;
}
iterator& iterator::operator++()
{
++_impl->iter;
return *this;
}
iterator& iterator::operator++( int )
{
++_impl->iter;
return *this;
}
iterator::reference iterator::operator*() const
{
// This is where the magic happens: We convert the shared_ptr to a raw pointer.
return _impl->iter->second.get();
}
bool operator==( const iterator& it1, const iterator& it2 )
{
return *it1 == *it2;
}
bool operator!=( const iterator& it1, const iterator& it2 )
{
return !( it1 == it2 );
}
Then all you need is a way to create these. To that end my_container's begin()/end() functions would look like:
my_container::iterator my_container::begin()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.begin();
return iterator( &iter );
}
my_container::iterator my_container::end()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.end();
return iterator( &iter );
}
That type erasure and taking the address of a member variable is a little dicey looking, but it's okay since we're immediately reinterpreting the void* and dereferencing and copying it.