tags:

views:

107

answers:

3

Hello everyone!

I often come accross the problem that I have a class that has a pair of Register/Unregister-kind-of-methods. e.g.:

class Log {
public:
    void AddSink( ostream & Sink );
    void RemoveSink( ostream & Sink );
};

This applies to several different cases, like the Observer pattern or related stuff. My concern is, how safe is that? From a previous question I know, that I cannot safely derive object identity from that reference. This approach returns an iterator to the caller, that they have to pass to the unregister method, but this exposes implementation details (the iterator type), so I don't like it. I could return an integer handle, but that would require a lot of extra internal managment (what is the smallest free handle?). How do you go about this?

A: 

You could manage your objects using smart pointers and compare the pointers for equality inside your register / deregister functions.

If you only have stack allocated objects that are never copied between an register and deregister call you could also pass a pointer instead of the reference.

You could also do:

typedef iterator handle_t;

and hide the fact that your giving out internal iterators if exposing internal data structures worries you.

Sebastian
Smart-pointers are used for shared ownership of objects, which is not the case here. Also, I think what was discussed in the other question I reference applies to smart-pointers as well.
Space_C0wb0y
Sorry, my bad, smart pointer are not the same as shared_ptr, so ignore that part of my comment.
Space_C0wb0y
And the answer was posted in the other question as well. As long as you compare pointers of the same type (say ISink) everything is good.
Sebastian
+2  A: 

You are safe unless the client object has two derivations of ostream without using virtual inheritance.

In short, that is the fault of the user -- they should not be multiply inheriting an interface class twice in two different ways.

Use the address and be done with it. In these cases, I take a pointer argument rather than a reference to make it explicit that I will store the address. It also prevents implicit conversions that might kick in if you decided to take a const reference.

   class Log {
   public:
       void AddSink( ostream* Sink );
       void RemoveSink( ostream* Sink );
   };

You can create an RAII object that calls AddSink in the constructor, and RemoveSink in the destructor to make this pattern exception-safe.

Matthew Herrmann
A: 

In your previous question, Konrad Rudolph posted an answer (that you did not accept but has the highest score), saying that everything should be fine if you use base class pointers, which you appear to do.

mxp