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.
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:
(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.
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:
( : )
-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.
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.