【问题标题】:Object Oriented way to iterate through a std::vector?迭代std :: vector的面向对象的方法?
【发布时间】:2010-10-12 01:28:14
【问题描述】:

我有一个类,它有一个 std::vector 的子控制指针。出于显而易见的原因,我不希望该类的用户直接访问 std::vector。我想要的只是一种给调用者指针的方法。什么是一个好的OO方式来做到这一点? (这个函数会经常被调用)

谢谢

【问题讨论】:

  • 你应该忘记“OO”是“好”的另一个词。通常不是。具体来说,STL 并不是特别面向对象,但它 设计得很好。你问的问题与 OOP 关系不大,你得到的答案也不是真正的“面向对象”的解决方法。迭代器正是这样做的正确方法。这比它们是否是“OOP 方式”更重要。:)
  • 嗯,“好”中嵌入了“oo”:-)

标签: c++ oop vector


【解决方案1】:

提供一个向向量返回const_iterator 的函数。添加一个以将迭代器返回到向量的末尾也很有用。

class MyClass {
public:
  typedef vector<T>::const_iterator c_iter;

  c_iter getBegin() const {return v.begin();}
  c_iter getEnd() const {return v.end();}

  // and perhaps if it's useful and not too invasive.
  const T& getAt(int i) const {return v.at(i);}

  //stuff
  vector<T> v;
};

【讨论】:

  • 所以在得到这些之后,用户可以为(it; it != itend; ++it) {}
  • @Milo:是的,没错。但是他们会被阻止改变向量;他们只能阅读它。
  • 一件事,对于getAt,如果傻用户输入了无效的索引怎么办?
  • 通常更好地为用户提供 typedef,因此他们不太可能将 vector&lt;T&gt;::const_iterator 硬编码为他们将检索到的迭代器放入的变量的类型,这需要编辑客户端代码应该 MyClass 更改其数据表示。
  • @Milo:对于 getAt,您应该进行一些错误检查。我只是在这里说明一个想法......
【解决方案2】:

迭代器是一种很好的、​​显而易见的方法。访问者模式是让客户端代码能够对向量中的每个元素进行操作的另一种方式:在某些方面它甚至更简洁,向用户暴露的更少,并允许容器更多的控制,例如:

  • 客户端有可能在以后失效的迭代器没有问题
  • 在允许其他线程对容器进行操作之前,在客户端代码读取所有条目之前获取互斥锁
  • 如果过滤或合成元素,则无需创建复杂的迭代器代理对象

但是

  • 客户端更强烈地锁定在您提供的任何迭代中:例如您通常可以通过容器步进多个独立的迭代器,以方便对多个元素的操作,但访问者通常在返回之前运行一次:任何额外的功能 - 暂停/恢复迭代,删除元素 - 需要容器的访问代码特别支持(也许通过访问者函数的返回码)。 (即使没有明确的支持,终止迭代也可能通过异常来实现)。相比之下,对于迭代器,可以在迭代器上使用单个擦除函数,无论是从 begin() 开始还是递增或不递增,以及像 find() 这样的其他操作:这是一种更简洁的功能分解。

看起来像:

class Container
{
  public:
    template <typename Visitor>
    void visit(Visitor& visitor)
    {
        for (Vector::const_iterator i = v_.begin(); i != v_.end(); ++i)
             visitor(*i);
    }

  private:
    typedef std::vector<X> Vector;
    Vector v_;
};

// client code...

struct Visitor
{
    void operator()(const X&) { ... }
    // any data you want to update as you iterate...
};

Visitor v(...any construction arguments...);
container.visit(v);

【讨论】:

    【解决方案3】:

    我通常会这样做:

    class MyClass {
    public:   
      const unsigned int GetNumberOfItems() { return v.size(); }
    
      T* GetItemNumber(const unsigned int n) 
      {
        // 3 options here, thrown your own exception type, or use the std one, or
        // or just return NULL meaning nothing there or out of range.
        try{
          return v.at(n);
        } catch (std::out_of_range &e){
        }
    
        return NULL;    
      }
    
      vector<T> v;
    };
    

    然后你可以这样做:

    MyClass cl;
    int count = cl.GetNumberOfItems();
    for (int i = 0; i < cl.GetNumberOfItems(); i++){
      T* item = cl.GetItemNumber(i);
    }
    

    不需要到外部世界的迭代器。如果您曾经不得不向标准 C API 公开这样的内容,那么公开就很容易了。

    【讨论】:

    • 在这里列出这个答案当然很好,但也存在一些问题:需要随机访问的接口确实使得在没有客户端代码突然受到性能影响的情况下改变实现变得更加困难,所以它是最好还是提供这些函数以及 begin()/end() 迭代器和/或访问者支持。更常见的称呼是 size(), T& operator[](size_t) / const T& operator[](size_t) const.
    猜你喜欢
    • 2020-04-18
    • 1970-01-01
    • 2012-05-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 2021-02-17
    • 1970-01-01
    • 2011-01-13
    相关资源
    最近更新 更多