Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing a std::list as read only

I have a class that contains, among other things, an std::list. I want to expose this list but only in such a way that the structure and the data it contains are read only, but still can be used with iterators.

The way I've got it 'working' atm is to return a copy of the list. This leave my class 'safe' but of course does nothing to stop the caller from modifying their copy of the list and not getting the right data.

Is there a better way?

like image 227
Tim Jones Avatar asked Jun 04 '10 18:06

Tim Jones


5 Answers

Why not return a const std::list& instead?

like image 77
JaredPar Avatar answered Nov 04 '22 11:11

JaredPar


Instead of exposing the list itself (at all) just expose const_iterators to its beginning and end. See cbegin() and cend() for help in doing this...

like image 44
Jerry Coffin Avatar answered Nov 04 '22 12:11

Jerry Coffin


Return a const reference:

const std::list<T>& getList() const;

or just return const iterators:

std::list<T>::const_iterator getListBegin() const;
std::list<T>::const_iterator getListEnd() const;
like image 28
James McNellis Avatar answered Nov 04 '22 12:11

James McNellis


There is a dependency issue in exposing one's data member to the outside world.

If you decide to change your attribute for something better (because list are the last resort container), or because you have a new requirements, then all your clients will be impacted, and that is bad.

One simple alternative is to offer a typedef:

typedef std::list<Foo>::const_iterator const_iterator;

IF your clients use your alias, then it's a simple matter of recompiling the code.

Another alternative is to create your own iterator class (not that difficult) which will embed the actual iterator.

class const_iterator
{
public:

private:
  typedef std::list<Foo>::const_iterator base_type;
  base_type mBase;
};

You simply forward all the operations to the actual iterator, and your clients (though they will have to recompile if you change your container) cannot accidentally use an unaliased type.

Then, the 3rd solution is similar to the first, except that you abstract the type... it's quite inefficient though (for a list), so I would not really advise it: iterators are supposed to be cheap to copy, you don't want to new anything.

like image 4
Matthieu M. Avatar answered Nov 04 '22 12:11

Matthieu M.


class foo {
  private:
     typedef std::list<bar> bar_cont_t;

  public:
     typedef bar_const_t::const_iterator bar_const_iterator;

     bar_const_iterator bar_begin() const {return bar_data_.begin();}
     bar_const_iterator bar_end  () const {return bar_data_.end  ();}

     // whatever else

  private:
     bar_cont_t bar_data_;
};
like image 3
sbi Avatar answered Nov 04 '22 11:11

sbi