Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allowing access to container objects in C++

Tags:

c++

I have a class like the one below:

class Foo {
private:
    std::map<std::string, Bar*> bars_by_name;
    std::map<std::string, Baz*> bazs_by_name;
};

Now I'd like to allow the user to access both collections but hide the implementation detail that I'm storing the objects into std::maps. Instead, I'd like to have member functions that return e.g. const iterators for the collections and possibly even a custom iterator that returns objects from both collections, since Bar and Baz belong to the same class hierarchy. Considering style, what would be the proper way to do this in C++? In Java, I'd probably set the method's return type to Iterable or wrap the collection into an unmodifiableCollection.

like image 335
tsnorri Avatar asked Jun 19 '12 22:06

tsnorri


2 Answers

You could pass the collection's iterators off as your own.

class Foo {
  private:
    typedef std::map<std::string, Bar*> BarContainer;
    typedef std::map<std::string, Baz*> BazContainer;
  public:
    typedef BarContainer::const_iterator ConstBarIterator;
    ConstBarIterator beginBars() { return bars_by_name.cbegin(); }
    ConstBarIterator endBars()   { return bars_by_name.cend(); }
    typedef BazContainer::const_iterator ConstBazIterator;
    ConstBazIterator beginBazs() { return bazs_by_name.cbegin(); }
    ConstBazIterator endBazs()   { return bazs_by_name.cend(); }

  private:
    BarContainer bars_by_name;
    BazContainer bazs_by_name;
};

This saves you the work of implementing your own iterator, yet leaves you the flexibility of changing your container types later and only requiring callers to recompile.

It doesn't solve the problem of iterating them both together.

like image 99
Adrian McCarthy Avatar answered Sep 20 '22 10:09

Adrian McCarthy


Create an interface that hides the details:

class Foo {
    public:
        void AddBar(std::string name, Bar*);
        void RemoveBar(std::string name);

        Bar* GetBar(std::string name);
        Bar* GetBar(std::string name) const;

        void AddBaz(std::string name, Baz*);
        void RemoveBaz(std::string name);

        Baz* GetBaz(std::string name);
        Baz* GetBaz(std::string name) const;

    private:
        std::map<std::string, Bar*> bars_by_name;
        std::map<std::string, Baz*> bazs_by_name;
};

For all the user knows, you could be storing them in a std::pair inside a list, inside a vector, or even using parallel arrays...anything really.

EDIT:

The fact that you are storing it in a map but want the user to iterate over the underlying container smells of a bad/incorrect design. Either only allow them individual access as above or create methods that will do this for them.

like image 44
Casey Avatar answered Sep 19 '22 10:09

Casey