Am I exposing too many iterators?
My class can have children and so I need to expose iterators. The render class needs to reverse iterate them which is why I have reverse iterators. But is there a way to have less of these because it seems like a lot:
std::vector<AguiWidget*>::iterator getChildBeginIterator();
std::vector<AguiWidget*>::reverse_iterator getChildRBeginIterator();
std::vector<AguiWidget*>::iterator getChildEndIterator();
std::vector<AguiWidget*>::reverse_iterator getChildREndIterator();
std::vector<AguiWidget*>::const_iterator getChildBeginIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator getChildRBeginIterator() const;
std::vector<AguiWidget*>::const_iterator getChildEndIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator getChildREndIterator() const;
std::vector<AguiWidget*>::iterator getPrivateChildBeginIterator();
std::vector<AguiWidget*>::reverse_iterator getPrivateChildRBeginIterator();
std::vector<AguiWidget*>::iterator getPrivateChildEndIterator();
std::vector<AguiWidget*>::reverse_iterator开发者_StackOverflow社区 getPrivateChildREndIterator();
std::vector<AguiWidget*>::const_iterator getPrivateChildBeginIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator getPrvateChildRBeginIterator() const;
std::vector<AguiWidget*>::const_iterator getPrivateChildEndIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator getPrivateChildREndIterator() const;
Thanks
Those look fine to me. Or I can't comment more precisely without knowing what exactly you're doing. But one thing you can surely do at any rate: why don't you use typedef
? If you use typedef
, you can use it outside the class as well, means in the client code, where you would use the class!
For example,
class sample
{
public:
//make these typedef public so you use it from outside!
typedef std::vector<AguiWidget*>::iterator iterator ;
typedef std::vector<AguiWidget*>::reverse_iterator reverse_iterator;
typedef std::vector<AguiWidget*>::const_iterator const_iterator;
typedef std::vector<AguiWidget*>::const_reverse_iterator const_reverse_iterator;
iterator child_begin();
reverse_iterator child_rbegin();
iterator child_end();
reverse_iterator child_rend();
const_iterator child_begin() const;
const_reverse_iterator child_rbegin() const;
const_iterator child_end() const;
const_reverse_iterator child_rend() const;
};
//Usage
sample s;
sample::iterator it= s.child_begin();
//see this ^^^^^^^^ how we use the typedef here!
This looks better! Basically the typedef
encapsulates the implementation, because it helps you hide the implementation detail of the class; for example, what container you're using in the class, std::vector
, std::list
, or what? See again the Usage illustration above; just by looking at it you cannot say the container type, can you?
Note also that I changed the function name as well. I think that is fine. After all, STL uses just begin
and end
as opposed to beginIterator
, and endIterator
. However, the lower-case is my taste, you may still prefer upper-case for consistency!
In my opinion, the const
functions don't make much sense, you probably would like the following set of functions if you want to expose readonly iterators!
const_iterator readonly_child_begin();
const_reverse_iterator readonly_child_rbegin();
const_iterator readonly_child_end();
const_reverse_iterator readonly_child_rend();
//Usage
sample s;
sample::const_iterator cit= s.readonly_child_begin();
//see this ^^^^^^^^ how we use the typedef here!
No canned answer, but ask yourself the following questions:
- Are you planning to use each and every one of the iterators?
- Do you need the difference between a child and a private child?
- Do you need to expose something called a private child?
- Wouldn't indexing be more convenient than iteration?
Otherwise, this seems perfectly fine; iterators can require a lot of code in the class, but make it convenient to use. (You should see my polymorphic XQuery-supporting iterators for XML collections which wrap several different non-STL-compliant iterators from third-party libraries...)
The typedef
suggestion put forward by several commenters is a good idea, because it gives better encapsulation, even though that encapsulation is not enforced by the compiler.
A simple way to cut this interface in half is to use ranges instead of begin()/end() pairs. For example, see Boost's iterator_range. Or you can return a reference to the container, but then you can either only get const_iterator
s or you can let the outside modify the container, which you may not want.
Note that vector iterators are bi-directional, so you can half the number of methods again, without exposing less functionality. If you really need to use ++
instead of --
, you can use a facade for that, for example Boost's reverse_iterator.
Note that both those things (ranges and reverse_iterator facades) are conceptually simple, and you can easily build them yourself (although doing it properly probably requires substantial work).
Either way, 4 functions instead of 16 sounds very reasonable to me.
You could achieve this via a mixin interface:
template<int Tag> class IteratorRange {
private:
std::vector<AguiWidget*>& range;
public:
IteratorRange(std::vector<AguiWidget*>& range) : range(range) {}
typedef std::vector<AguiWidget*>::iterator iterator ;
typedef std::vector<AguiWidget*>::reverse_iterator reverse_iterator;
typedef std::vector<AguiWidget*>::const_iterator const_iterator;
typedef std::vector<AguiWidget*>::const_reverse_iterator const_reverse_iterator;
iterator begin();
reverse_iterator rbegin();
iterator end();
reverse_iterator rend();
};
enum { PublicChild, PrivateChild };
class MyClass : public IteratorRange<PublicChild>, private IteratorRange<PrivateChild> {
MyClass() : IteratorRange<PublicChild>( ??? ), IteratorRange<PrivateChild>( ??? ) { }
typedef IteratorRange<PublicChild> Public; // For MyClass::Public::begin etc
};
Depending on what you are doing with those iterators, you can expose some kind of visitor functions:
template<class UnaryFunction>
void visit(UnaryFunction & f) {
// apply function to each element in vector
}
You can tweak that signature according to you needs, but it might make for a slimmer interface.
Yes, mostly. Standard library containers do this too. Welcome to C++!
However, I'd reconsider the "private" ones. I can't even tell what they're supposed to be, but it can't be good.
I'd also add some member type aliases with the typedef
keyword to make the return type more encapsulated.
You already expose std::vector::iterator, that means you already exposes many implementation details.
Return const std::vector&, const reference can reduce most of the interface and provide more functionalities. Otherwise, in future you may need to add something like getChildSize(), getXXXSize() etc.
精彩评论