【问题标题】:How to iterate over private member std::vector most efficient?如何迭代私有成员 std::vector 最有效?
【发布时间】:2014-05-22 09:57:10
【问题描述】:

我正在编写一个实现双向链接树的类,我希望用户能够尽可能高效地迭代节点的所有子节点。

Tree.h(缩短):

我省略了迭代中不涉及的所有内容。

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_;
};

现在,我在这里看到了两种可能性:

1。基于索引的迭代

(假设static unique_ptr&lt;Tree&gt; root已经构造好了)

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

这看起来很简单并且可以保存,因为用户无法访问底层结构。此外,没有太多开销,因为每当修改 Tree 时,children_-vector 的长度都会保存到一个变量中(请相信我),并且内联获取此长度的函数。

2。基于迭代器的迭代

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
}

这看起来既危险又丑陋,但我可以想象它会更快。我的问题是用户可以将 unique_ptr 移动到其他地方,我想尽可能限制使用以避免错误使用结构。

什么是更有效的方法,它真的有多大的不同?


PS:这是我在写这篇文章时想到的第三种方法,但我现在知道如何实现它:

3。对于( : )-loop

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

语法方面,它看起来非常简单,但我不知道for ( : )-loop 真正在内部做什么(可能与迭代器一起使用,如果没有-&gt;begin()-&gt;end() Tree 上的函数,则无法使用) .

额外问题:是否可以为我自己的班级实现这种模式?

【问题讨论】:

  • 对于基于范围的 for 循环,请参见例如this reference。它还显示了一个等效的实现。我认为使用它是首选的替代方法。
  • "...没有太多开销,因为只要修改了 Tree,children_-vector 的长度就会保存到一个变量中(相信我)和函数这个长度是内联的。” - 对不起,不能相信你。 vector 记录它们当前的大小并支持 O(1) 内联查询...重复会降低性能。更一般地说,Joachim 是对的——只需提供 begin()end(),人们可以使用 range-for 表示法;如果需要,您的迭代器可以隐藏unique_ptr。也可以轻松支持visitation function
  • @JoachimPileborg This reference仅表明我的想法行不通,因为children_是私有的(应该保持这种状态),我将再次向用户公开unique_ptrs,这不是真的。无赖。
  • @TonyD 我将如何实现begin()end() 函数?你能指出我的来源吗?
  • @iFreilicht: "reference only shows..." - 只需将 vector&lt;unique_ptr&lt;Tree&gt;&gt;::iterator children_begin() 重命名为 begin()children_end() 重命名为 end() 即可,或者编写自己的 Iterator 类为了更好地封装 - 它应该存储 vector&lt;...&gt;::iterator 并取消引用产生 Tree&amp;,而不是 unique_ptr

标签: c++ for-loop stl indexing iterator


【解决方案1】:

根据@WojitekSurowka 的建议,我查看了boost indirect iterator,这似乎是一个不错的选择。但是,它似乎与 VS2013 配合不太好,所以我不得不自己编写一个迭代器,这并不像我最初想象的那么难。

根据@JoachimPileborg 发布的this reference,这使我能够充分利用for ( : )-loop。但是请注意,我的解决方案使用了一个完全自定义的迭代器,它不是从任何 STL 向量派生的,因此不能保证正确处理所有 STL 操作。

树.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_;
};

所以现在这使我能够执行以下操作:

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

太棒了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-02
    • 2011-04-14
    • 2019-08-31
    • 2019-01-16
    • 1970-01-01
    • 2017-02-01
    • 2020-11-22
    • 2020-09-13
    相关资源
    最近更新 更多