开发者

Am I exposing too many iterators?

开发者 https://www.devze.com 2023-02-16 10:25 出处:网络
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

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_iterators 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消