views:

188

answers:

6

Consider the piece of code:

class Foo
{ // ...
    std::vector<Bar> bars;
};

Should I expose the whole container, or should I expose typedef'd iterator class and write adaptor methods (begin(), end(), size(), and whatever I need)?

If the answer is it depends, how should one make a decision?

+5  A: 

You only expose iterators or iterator-ranges (and typedefed iterator types) unless you absolutely need to expose the container.

This avoids breaking other code when changing the implementation details and also follows the guide-lines of information hiding / encapsulation.

Georg Fritzsche
Is it OK with the overhead of reimplementing access methods in every single class?
goodrone
What kind of access methods do you need, beyond iterators?
jalf
Without any specific case and in general, yes. It is way better than potentially changing code in n client classes compared to changing some minor implementation details in your container-encapsulating class. Maybe later you don't want to expose all the contents of the container - then you just switch to exposing filtering iterators and the client code shouldn't be affected at all.
Georg Fritzsche
@goodrone: Typical access functions like `begin()` are `inline`, so there is no overhead. It's pure syntax.
sbi
+2  A: 

The answer is "it depends". :)

How much does the user of your class need to know about the internal vector?

Take as an example the stack class in the standard library. It uses a standard container internally, but it doesn't even expose iterators. It exposes push and pop methods, and little else.

The user of the class doesn't need to know anything else. The user of the class shouldn't be allowed to access the middle of the stack. If you do that, it is no longer a stack.

Quite often, users of the class don't actually need anything more than iterators. If your Foo class exposes begin() and end(), what else do I need? I'm able to access every element of the internal data structure at will. I can modify each of them, or read each of them.

Do I also need to be able to insert/remove elements? Perhaps, and if so, should I do that directly on the vector, or through your adaptor methods? It depends. What does the object represent? Does it make sense to add objects directly to the vector owned by a Foo? Or does it make sense to add objects to the Foo object, and let it delegate to whatever internal implementation it likes?

jalf
+1  A: 

It depends.
If by changing the state of a pub in the container you are changing the state of the object foo then you should not expose the container (as you may want to limit accesses at a later stage, or make a note of accesses).

If on the other hand foo objects are just a glorified container that also holds a vector then maybe exposing the vector is OK (note the maybe). Note by exposing the vector you are tightly binding the user of foo to a std::vector and thus changing it to another container type at a latter stage may be harder.

Martin York
A: 

In case you only need to read from your vector, expose the .begin() .end() and iterator. In case you actually need to add/remove data or change it, expose the entire vector.

Ruud v A
A: 

Don't expose nothin, wrap the call using thin-wrapper techniques and check for some obvious such as null and index out of bounds as well as type and sanity checks.

See coding horrors here on this site for why, the coder at M$ who found his function call posted on ZD net as patch of the week. If you have to write hand-tweaked zero-time returns and expose it to other sections, you might get hammered by Section 8's

Nicholas Jordan
+1  A: 

If you want to follow good design lines,

your map should private or protected, And it should look like

class Foo
{  
    public:
    typedef std::vector<Bar>::const_iterator FooIterator;
    FooIterator begin()
    {
      return bars.begin();
    }
    FooIterator end()
   {
    return bars.end();
   }
    protected:
    std::vector<Bar> bars;

};

int main()
{
   Foo obj;
   for(obj::FooIterator itr=obj.begin();itr!=obj.end();itr++)
  {
  }
}
sat