简体   繁体   中英

How to iterate over private member std::vector most efficient?

I am writing a class that implements a doubly linked tree, and I want users to be able to iterate over all children of a node as efficient as possible.

Tree.h (shortened):

I left out everything that isn't involved in the iteration.

class Tree
{
public:
    static Tree& root(){ return *root_; }
    //index-based iteration
    inline unsigned int numChildren() const{ return childrenSize_; }
    Tree& getChild(unsigned int index) const;                       

    //iterator-based iteration                          
    vector<unique_ptr<Tree>>::iterator children_begin(){
        return children_.begin();
    }
    vector<unique_ptr<Tree>>::iterator children_end(){
        return children_.end();
    }

private:
    vector<unique_ptr<Tree>> children_;
    unsigned int childrenSize_;
    Tree* parent_;

    static unique_ptr<Tree> root_;
};

Now, I see two possibilities here:

1. Index-based iteration

(suppose the static unique_ptr<Tree> root has been constructed)

Tree& myTree = Tree::root();
for(int i = 0; i < myTree.numChildren(); ++i;){
    Tree& child = myTree.getChild(i);
    //do stuff with child
}

It seems straight-forward and save, as the user has no access on the underlying structure. Also, there is not much overhead, because the length of the children_ -vector is saved to a variable whenever the Tree is modified (just trust me on that one) and the function to get this length is inlined.

2. Iterator-based iteration

Tree& myTree = Tree::root();
for(vector<unique_ptr<Tree>>:iterator it = myTree.children_begin(); it < myTree.children_end(); ++it; ){
    Tree& child = (*(*it));
    //do stuff with child
}

This seems dangerous and ugly, but I could imagine it to be faster. My problem here is that the user could just move the unique_ptr to somewhere else and I want to restrict usage as much as possible to avoid false usage of the structure.

What is the more efficient approach and how much difference does it really make?


PS: Here is a third approach I came up with while writing this, but I have now idea how to implement it:

3. The for ( : ) -loop

for (Tree& child : Tree::root()){
    //do stuff with child
}

Syntax-wise, it just looks amazingly simple, but I have no idea what the for ( : ) -loop really does internally (probably work with iterators and wouldn't work without ->begin() and ->end() functions on Tree).

Bonus question: Would it be possible to implement this pattern for my own class?

As suggested by @WojitekSurowka, I looked into the boost indirect iterator , and it seems to be quite a good option. However, it seems to not work that well together with VS2013, so I had to write an iterator myself, which wasn't as hard as I first thought.

This enables me to use the for ( : ) -loop in all its glory, according to this reference as posted by @JoachimPileborg. Note, however, that my solution uses a completely custom iterator that does NOT derive from any STL-vector and is therefore not guaranteed to work with all STL-operations correctly.

Tree.h:

class Tree;

class TreeIterator
{
public:
    TreeIterator(std::vector<unique_ptr<Tree>>::iterator& pos) :
    pos_(pos)
    {};

    bool operator==(const TreeIterator& rhs) { return pos_ == rhs.pos_; }
    bool operator!=(const TreeIterator& rhs) { return pos_ != rhs.pos_; }
    bool operator<=(const TreeIterator& rhs) { return pos_ <= rhs.pos_; }
    bool operator>=(const TreeIterator& rhs) { return pos_ >= rhs.pos_; }
    bool operator<(const TreeIterator& rhs) { return pos_ < rhs.pos_; }
    bool operator>(const TreeIterator& rhs) { return pos_ > rhs.pos_; }

    void operator ++(){ ++pos_; }

    //this is the most important thing!!!
    Tree& operator*(){
        return **pos_; //double-dereferencing
    }

private:
    std::vector<unique_ptr<Tree>>::iterator pos_;
};


class Tree
{
public:
    static Tree& root(){ return *root_; }                   

    //regular begin and end functions for iterators, used by the `for( : )`-loop        
    TreeIterator& begin(){
        return *(beginIter_ = std::move(unique_ptr<TreeIterator>(new TreeIterator(children_.begin()))));
    }
    TreeIterator& begin(){
        return *(endIter_ = std::move(unique_ptr<TreeIterator>(new TreeIterator(children_.end()))));
    }

private:
    vector<unique_ptr<Tree>> children_;
    Tree* parent_;
    unique_ptr<TreeIterator> beginIter_;
    unique_ptr<TreeIterator> endIter_;
    static unique_ptr<Tree> root_;
};

So this now enables me to do the following:

for(Tree& child : Tree::root()){
    //do stuff with child
}

It's awesome!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM