views:

113

answers:

4

I'm new to c++ but have set my mind on a specific task that needs me to enable adding a specific chunk of code to be execute whenever any list item is attempted to be changed or read.

The resulting list should behave and look as much as as possible to std::list except for this small exception that would enable me to execute a specific tasks whenever this list is about to be read/written to.

From what i have found out so far, all i could think of is deriving a class from list::iterator and overloading it's operator* and operator= to implement these specific tasks.

Then i should derive a class from std::list and make it use my new iterator type by overloading begin() and end() methods. Or is there a better way of making it use a custom iterator?

That would handle the iterator access but I can see lists can even return pointers to it's members. I guess there is nothing i can do about them and will have to remove this feature from my new list class.

I would appreciate your oppinion on this subject.

+5  A: 

Deriving from std::list is almost certainly not the answer. The collections in stl are simply not meant to be derived from and doing so will cause you problems down the road.

The classic example of why is the destructor problem. The destructors in stl collections are not virtual. This will break any logic you place in your derived class destructor if an object is deleted via a reference to the std::list. For example

std::list* pList = new YourNewListClass();
delet pList; // runs std::list::~list()

You'd also need to override much more than the methods on the iterator. It would require changing every method which can possibly mutate the collection.

A more stable approach would be to implement your own std::list style class which follows the standard stl container behavior. You could then include use this list in the places you wanted events without running into the problems with deriving from std::list.

JaredPar
In fact your new class could contain a private std::list member that does most of the work.
Alan
Thanks Jared, the more i think of it, the more i think you are right.Alan - yes that's the simple solution but does not really solve my initial problem. especially if i want to keep the apearence and behavior of std::list.
joza101
The idea is that you create your own list, say joza::list which uses a std::list internally. Every operation on joza::list actually just forwards to the same operation for std::list, except for reads and writes; which will first do what you want to and then call the associated std::list operation.
Max Lybbert
+1  A: 

Look at the way that std::deque implements it's functionality as an adaptor of another standard collection. This is the way to go -- use composition not inheritance and wrap the underlying collection to provide your new facilities. For bonus points template your implementation on the underlying collection. For many uses a std::vector will outperform a std::list and your additions should be able to work equally well with whichever of these the user chooses.

KayEss
+1  A: 

First things first..NEVER derive a class from STL containers as they are not meant to be derived. For starters their destructor is not virtual.

The easiest way would be contain a std::list in your own class and providing list like interfaces. In these list like interfaces you can provide any additional tasks you want to perform before/updating the list.

Also, take a look at this design pattern: Decorator

Naveen
+1  A: 

Take a look at boost::transform_iterator<>. It seems to be close to what you're looking for. It calls a functor whenever the operator*() function of the transform_iterator<> is called. The intended use is to transform the object the iterators points to, but there's nothing that says the functor can't do something else and simply return the original value of the pointed to object.

Even if it's not quite what you want, it will probably provide ideas to how you might approach your problem.

Michael Burr