Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object Oriented way to iterate through a std::vector?

Tags:

c++

oop

vector

I have a class which has a std::vector of child control pointer. For obvious reasons, I do not want the user of the class to have direct access to the std::vector. All I would want is a way to give the caller the pointers. What would be a good OO way to do this? (this function will be called often)

Thanks

like image 376
jmasterx Avatar asked Oct 12 '10 01:10

jmasterx


3 Answers

Provide a function that returns a const_iterator to the vector. It is also useful to add one to return the iterator to the end of the vector.

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;
};
like image 56
JoshD Avatar answered Oct 07 '22 04:10

JoshD


Iterators are a good, obvious way to do this. A visitor pattern is another way to give the client code the ability to operate on each element in the vector: in some ways it's even cleaner, exposing less to the user, and allowing the container more control, e.g.:

  • there's no issue with the client having iterators that might be later invalidated
  • to obtain a mutex lock until the client code had read all entries before other threads are allowed to operate on the container
  • if you filter or synthesize the elements, you don't need to create complicated iterator proxy objects

BUT

  • the client is more strongly locked into whatever iteration you provide: e.g. you can generally step multiple independent iterators through a container, facilitating operations on multiple elements, but visitor typically runs through once before returning: any extra functionality - suspending/resuming iteration, deleting an element - needs to be specifically supported by the container's visit code (perhaps by a return code from the visitor function). (Even without explicit support, terminating iteration might be achieved by an exception). By way of contrast, with iterators a single erase function can be used on an iterator whether from begin(), incremented or not, as well as other operations like find(): this is a cleaner factoring of functionality.

That would look something like:

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);
like image 44
Tony Delroy Avatar answered Oct 07 '22 04:10

Tony Delroy


I usually do it something like the following:

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

Then you can just do something like:

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

No iterators to the outside world required. If you have ever have to expose something like this to a standard C API then it's very easy to expose.

like image 24
hookenz Avatar answered Oct 07 '22 05:10

hookenz