I have a class that contains, among other things, an std::list. I want to expose this list but only in such a way that the structure and the data it contains are read only, but still can be used with iterators.
The way I've got it 'working' atm is to return a copy of the list. This leave my class 'safe' but of course does nothing to stop the caller from modifying their copy of the list and not getting the right data.
Is there a better way?
Why not return a const std::list& instead?
Instead of exposing the list itself (at all) just expose const_iterators to its beginning and end. See cbegin() and cend() for help in doing this...
Return a const reference:
const std::list<T>& getList() const;
or just return const iterators:
std::list<T>::const_iterator getListBegin() const;
std::list<T>::const_iterator getListEnd() const;
There is a dependency issue in exposing one's data member to the outside world.
If you decide to change your attribute for something better (because list are the last resort container), or because you have a new requirements, then all your clients will be impacted, and that is bad.
One simple alternative is to offer a typedef:
typedef std::list<Foo>::const_iterator const_iterator;
IF your clients use your alias, then it's a simple matter of recompiling the code.
Another alternative is to create your own iterator class (not that difficult) which will embed the actual iterator.
class const_iterator
{
public:
private:
typedef std::list<Foo>::const_iterator base_type;
base_type mBase;
};
You simply forward all the operations to the actual iterator, and your clients (though they will have to recompile if you change your container) cannot accidentally use an unaliased type.
Then, the 3rd solution is similar to the first, except that you abstract the type... it's quite inefficient though (for a list), so I would not really advise it: iterators are supposed to be cheap to copy, you don't want to new anything.
class foo {
private:
typedef std::list<bar> bar_cont_t;
public:
typedef bar_const_t::const_iterator bar_const_iterator;
bar_const_iterator bar_begin() const {return bar_data_.begin();}
bar_const_iterator bar_end () const {return bar_data_.end ();}
// whatever else
private:
bar_cont_t bar_data_;
};
Related
I'm building an C++ API just for learning purposes and I'll need a class
with two containers. Suppose they're initially of type vector<>.
class MyClass
{
private:
std::vector<int> containerA;
std::vector<double> containerB;
public:
std::vector<int> *getContainerA();
std::vector<double> *getContainerB();
};
But if someday I'll have to change the type of these containers (to list, for instance), I would not want that the users of this API needed to change anything in their code.
So, considering that containerA and containerB types were changed, when using the getters above, which return vector<>, API users would need to change their code to match the new type of API's containers.
I think that return iterators instead of vector<> can help me with that.
class MyClass
{
private:
std::vector<int> containerA;
std::vector<double> containerB;
public:
std::vector<int>::iterator beginContainerA() { return containerA.begin(); }
std::vector<int>::iterator endContainerA() { return containerA.end(); }
std::vector<double>::iterator beginContainerB() { return containerB.begin(); }
std::vector<double>::iterator endContainerB() { return containerB.end(); }
};
Is this the way how things are done? Is there a better way? Thank you.
I recommend combining πάντα ῥεῖ's idea and yours: Both define a type alias, and return iterators:
typedef std::vector<int> ContainerAType;
ContainerAType::iterator beginContainerA();
Also, you'll need a const version as well, so that you can use the iterators on const instances:
ContainerAType::const_iterator beginContainerA() const;
Consider whether non-const access is even needed.
Consider returning a range of iterators instead:
boost::iterator_range<ContainerAType::iterator> containerA();
However, to truly protect the client from any changes, you will need to use type erasing iterator. With type erasure, the client does not even need to recompile, if you change from vector to list (but you also have to use pimpl pattern, to achieve that). You could even stop using a container entirely, and return custom iterators, that generate the data.
Boost has you covered: http://www.boost.org/doc/libs/1_54_0/libs/range/doc/html/range/reference/ranges/any_range.html Type erasing iterators do have some overhead, due to using a virtual function call internally.
Rather use getters like
const std::vector<int>& getContainerA() const { return containerA; }
and let your clients use
std::vector<int>::const_iterator
from the reference.
But if someday I'll have to change the type of these containers (to list, for instance), I would not want that the users of this API needed to change anything in their code.
Also you can use a class specific typedef to expose the underlying container type:
typedef std::vector<int> ContainerAType;
const ContainerAType& getContainerA() const { return containerA; }
Note that handing out iterators is bad, in the sense that these may become invalid upon changes to the underlying containers.
If a client should be notified about changes, you'll need a separate mechanism like e.g. an Observer .
I've read that hiding the actual container type used in a class is a good thing. It makes accidentally (or intentionally) writing code that conforms to the actual container type (that may change and break prior code) harder and thus the code is more robust.
Therefore, that means that this is not a good idea; If the implementation changes from a std::vector to a std::list things will break:
class MyType {
public:
const std::vector<MyType*>& GetChildren() const;
std::vector<MyType*>& GetChildren();
const std::vector<MyType*>::size_type GetChildCount() const;
std::vector<MyType*>::size_type GetChildCount();
private:
MyType* _parent;
std::vector<MyType*> _children;
};
Using a typedef makes it look like it's hidden, but a user only need to look at the mouse-over definition (at least in Visual Studio) to see the actual type and code accordingly. Even worse, the compiler doesn't see the typedef at all, it just sees the underlying type.
class MyType {
public:
typedef std::vector<MyType*> ChildContainer;
typedef ChildContainer::size_type ChildContainerSize;
const ChildContainer& GetChildren() const;
ChildContainer& GetChildren();
const ChildContainerSize GetChildCount() const;
ChildContainerSize GetChildCount();
private:
MyType* _parent;
ChildContainer _children;
};
Question: Is there a way to hide the underlying container type so that it will be difficult for the user to code around it short of wrapping the STL container in a potentially poorly written and not-needed wrapper?
Sometimes you have no choice, but to show the underlying container types. For example, for randomly accessed containers like std::vector v, I can do sort(v.begin(), v.begin()+10); whereas for a std::list li, I can only do li.sort(), how can you hide the container type, and only use iterators? These iterators are totally incompatible. Also, container type is one of the most fundamental consideration in software designing, why should you think about the future possibility of changing container type? That usually means bad designing, and total remake of the software.
You should use typedefs and iterators. Then if you want to change the container, you can because users are only dependent upon the iterators. People can always break it if they want, but you are exposing an easy way to NOT break.
class MyType {
public:
typedef std::vector<MyType*> ChildContainer;
typedef ChildContainer::size_type ChildContainerSize;
typedef std::vector<MyType*> ChildContainerIter;
typedef std::vector<MyType*> ChildContainerCIter;
const ChildContainerCIter GetChildrenBegin() const { return _children.begin(); }
ChildContainerIter GetChildrenBegin() { return _children.begin(); }
const ChildContainerCIter GetChildrenEnd() const { return _children.end(); }
const ChildContainerSize GetChildCount() const;
private:
MyType* _parent;
ChildContainer _children;
};
And no there is not a way to really hide it completely. The compiler needs to know what type it is when their code is compiled.
I have many class that has inside list<cats> , and i want to allow users that use my class to iterate over the cats i have, so i give them access to a const iterator by writing the following function :
list<cats>::const_iterator GetIterator()
{
//return the iterator
}
Now i want to change my implementation of the class and use vector instead of list, so i need to return a const iterator of vector.
The problem is that every one that uses my class now need to change their code from list<cats>::const_iterator to vector<cat>::const_iterator.
If all the iterators where to inherit from "forward iterator" it would be very useful.
My major question is how i solve this problem, i don't want to inherit from a collection.
Another very related question is why the designers of STL chose not to inherit iterators one from another ? (for example random access iterators could have inherit from forward iterators, he have all it's functionality)
I did pretty extensive search before i asked my question but couldn't find the solution.
The closest thing i found to my question is this, but it is not exactly my question.
Give access to encapsulated container
Expose an iterator type from your class, e.g:
class a
{
vector<int> g;
public:
typdef vector<int>::const_iterator const_iterator;
const_iterator begin() const
{ return g.begin(); }
: // etc
};
If the users can recompile the code you can use
typedef list<cats> CatList;
in your include file. Then, if you want to change the container, change it to
typedef vector<cats> CatList;
and the users would use e.g.
CatList::iterator it;
However this is not a good practice; the iterators of different containers may look equal but behave differently e.g. deleting an item from a vector invalidates all iterators but doing the same on list affects only iterator of deleted item. Not mentioning the case if some day you want to use std::map<some_key,cats>.
I agree with Nim's answer and wanted to offer the style I follow which I think makes maintenance easier since now changing the underlying container type requires no work at all for users of your class and very little work for you when maintaining your class.
class A
{
// change only this to change the underlying container:
// Don't Repeat Yourself!
using container_t = std::vector<int>;
public:
// constructor from a copy of a container
A(container_t source)
: _myContainer { std::move(source) }
{}
// this derived type is part of the public interface
using const_iterator_t = container_t::const_iterator;
// following your function naming style...
const_iterator_t GetIterator() const { return /* some iterator of _myContainer */; }
const_iterator_t GetEnd() const { return std::end(_myContainer); }
private:
container_t _myContainer;
};
Let's say I have a class:
class NumberCollection
{
public:
typedef std::set<int> SetType;
typedef SetType::iterator iterator;
void insert(int n);
iterator begin();
iterator end();
size_t size() const;
iterator difficultBegin();
iterator difficultEnd();
size_t difficultSize() const;
private:
SetType easySet_, difficultSet_;
}
Where insert() adds an element to easySet_. difficultSet_'s members change depending on the members of easySet_.
The problem I am having is that, multiple insertions means that difficultSet_ is constantly recalculated. So I want difficultSet_ to be calculated lazily (i.e., only when difficultBegin(), difficultEnd(), or difficultSize() are called). The problem is, then I actually have to make difficultSet_ into a mutable because otherwise difficultSize() cannot operate on it.
So now my class declaration looks like
class NumberCollection
{
public:
typedef std::set<int> SetType;
typedef SetType::iterator iterator;
void insert(int n);
iterator begin();
iterator end();
size_t size() const;
iterator difficultBegin();
iterator difficultEnd();
size_t difficultSize() const;
private:
SetType easySet_;
mutable SetType difficultSet_;
mutable bool upToDate_;
}
I feel like this is bad design though. Is there a better way?
That's totally the way to do it. Const can mean binary const, or it can mean conceptually const. Using mutable means you're doing the later, which is fine.
To help understand why to use mutable, we can explore other options.
You can solve the same problem using const_cast:
size_t NumberCollection::difficultSize() const
{
if(!upToDate_)
{
NumberCollection& nonConst = const_cast<NumberCollection&>(*this);
nonConst.difficultSet_ = PerformExpensiveCalculationFunction();
nonConst.upToDate_ = true;
}
// etc....
}
Having offered this solution, I'll say that it's inferior to using mutable. If a member is marked as mutable, then simply by looking at the header I can gather how you are treating it. I don't get this information if you use const_cast.
But then somebody might take the other side of the debate, and say that it's better not to expose implementation details in the header.
This is essentially the reason C++ has the mutable construct. Alan De Smet's rant about the misuse of mutable shows the kinds of situations in which mutable should not be used.
In this case, difficultSize() does not change what the NumberCollection represents - which is suitable for marking as const. It does how ever need to change the internals at times, which is why you need to mark difficultSet_ and upToDate_ as mutable.
Your solution is fine in C++98. Note that in C++11 you should consider to synchronize the access to your mutable data. Otherwise you may run into problems when you your class is used by the STL, which assumes that all const member functions are thread-safe.
For further details, see Does const mean thread-safe in C++11?
This question already has answers here:
Generic iterator
(3 answers)
Closed 8 years ago.
Suppose I want to implement in C++ a data-structure to store oriented graphs. Arcs will be stored in Nodes thanks to STL containers. I'd like users to be able to iterate over the arcs of a node, in an STL-like way.
The issue I have is that I don't want to expose in the Node class (that will actually be an abstract base class) which STL container I will actually use in the concrete class. I therefore don't want to have my methods return std::list::iterator or std::vector::iterator...
I tried this:
class Arc;
typedef std::iterator<std::random_access_iterator_tag, Arc*> ArcIterator; // Wrong!
class Node {
public:
ArcIterator incomingArcsBegin() const {
return _incomingArcs.begin();
}
private:
std::vector<Arc*> _incomingArcs;
};
But this is not correct because a vector::const_iterator can't be used to create an ArcIterator. So what can be this ArcIterator?
I found this paper about Custom Iterators for the STL but it did not help. I must be a bit heavy today... ;)
Try this:
class Arc;
class Node {
private:
std::vector<Arc*> incoming_;
public:
typedef std::vector<Arc*>::iterator iterator;
iterator incoming_arcs_begin()
{ return incoming_.begin(); }
};
And use Node::iterator in the rest of the code. When/if you change the container, you have to change the typedef in a single place. (You could take this one step further with additional typedef for the storage, in this case vector.)
As for the const issue, either define vector's const_iterator to be your iterator, or define double iterator types (const and non-const version) as vector does.
Have a look at Adobe's any_iterator: this class uses a technique called type erase by which the underyling iterator type is hidden behind an abstract interface. Beware: the use of any_iterator incurs a runtime penalty due to virtual dispatching.
I want to think there should be a way to do this through straight STL, similar to what you are trying to do.
If not, you may want to look into using boost's iterator facades and adaptors where you can define your own iterators or adapt other objects into iterators.
To hide the fact that your iterators are based on std::vector<Arc*>::iterator you need an iterator class that delegates to std::vector<Arc*>::iterator. std::iterator does not do this.
If you look at the header files in your compiler's C++ standard library, you may find that std::iterator isn't very useful on its own, unless all you need is a class that defines typedefs for iterator_category, value_type, etc.
As Doug T. mentioned in his answer, the boost library has classes that make it easier to write iterators. In particular, boost::indirect_iterator might be helpful if you want your iterators to return an Arc when dereferenced instead of an Arc*.
Consider using the Visitor Pattern and inverting the relationship: instead of asking the graph structure for a container of data, you give the graph a functor and let the graph apply that functor to its data.
The visitor pattern is a commonly used pattern on graphs, check out boost's graph library documentation on visitors concepts.
If you really don't want the client's of that class to know that it uses a vector underneath, but still want them to be able to somehow iterate over it, you most likely will need to create a class that forwards all its methods to std::vector::iterator.
An alternative would be to templatize Node based on the type of container it should use underneath. Then the clients specifically know what type of container it is using because they told them to use it.
Personally I don't think it usually makes sense to encapsulate the vector away from the user, but still provide most (or even some) of its interface. Its too thin of an encapsulation layer to really provide any benefit.
I looked in the header file VECTOR.
vector<Arc*>::const_iterator
is a typedef for
allocator<Arc*>::const_pointer
Could that be your ArcIterator? Like:
typedef allocator<Arc*>::const_pointer ArcIterator;
You could templatize the Node class, and typedef both iterator and const_iterator in it.
For example:
class Arc {};
template<
template<class T, class U> class Container = std::vector,
class Allocator = std::allocator<Arc*>
>
class Node
{
public:
typedef typename Container<Arc*, Allocator>::iterator ArcIterator;
typedef typename Container<Arc*, Allocator>::Const_iterator constArcIterator;
constArcIterator incomingArcsBegin() const {
return _incomingArcs.begin();
}
ArcIterator incomingArcsBegin() {
return _incomingArcs.begin();
}
private:
Container<Arc*, Allocator> _incomingArcs;
};
I haven't tried this code, but it gives you the idea. However, you have to notice that using a ConstArcIterator will just disallow the modification of the pointer to the Arc, not the modification of the Arc itself (through non-const methods for example).
C++0x will allow you do this with automatic type determination.
In the new standard, this
for (vector::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr
can be replaced with this
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)
By the same token, you will be able to return whatever iterator is appropriate, and store it in an auto variable.
Until the new standard kicks in, you would have to either templatize your class, or provide an abstract interface to access the elements of your list/vector. For instance, you can do that by storing an iterator in member variable, and provide member functions, like begin() and next(). This, of course, would mean that only one loop at a time can safely iterate over your elements.
Well because std::vector is guaranteed to have contiguous storage, it should be perfect fine to do this:
class Arc;
typedef Arc* ArcIterator;
class Node {
public:
ArcIterator incomingArcsBegin() const {
return &_incomingArcs[0]
}
ArcIterator incomingArcsEnd() const {
return &_incomingArcs[_incomingArcs.size()]
}
private:
std::vector<Arc*> _incomingArcs;
};
Basically, pointers function enough like random access iterators that they are a sufficient replacement.