tags:

views:

265

answers:

6

I've stumbled onto something I can't figure out, so I think I'm missing something in the greater C++ picture.

In short, my question is: how to keep a mutable, non-deletable, possibly NULL instance of an object in a class.

The longer version is:

I have the following scenario: a bunch of classes (which I can change slightly, but not thoroughly refactor), most of which need to use an object. This object, while mutable, is managed by someone else so it must not be deleted.

Some of the classes in the bunch do not need such an object - they reuse code from other classes, but through the available parameters supplied to these classes it is guaranteed that even if an object is supplied, it will not be used.

The current implementation uses a pointer-to-const-object (const Obj *). This, in turn, means all the object's methods must be const and most fields mutable. This is a messy solution since the fields declared mutable are available for inspection (so quite the opposite of the c++ lite entry here). It also only partially solves the "do-not-delete-this-here" issue (compiler does not complain but a const in front of the object is an indication).

If I used a reference to this object, I'd force some callers to create a "dummy" object and provide it to the class they are instantiating. This is also messy, besides being a waste of resources. I cannot create a global object to can stand in for a "NULL" reference due to project restrictions.

I feel that the reference is the tool I need, but I cannot refactor the classes involved to such an extent as to have the object disappear from their implementations where it is not used (it can be done, but it is not simple and it would not be fast). So I want to implement something simpler, which will just draw an alarm signal if anyone tries to misuse this object, but keeps my object mutable.

The best solution I can think of is using a const-pointer-to-object (Obj * const) - this does not make the compiler complain, but I have my mutable object and a sort-of alarm signal -through the const - in place as well.

Does anyone have a better idea ?

A: 

Sounds like a case for a shared_ptr.

Patrick
No, it allows the using code to retrieve the raw pointer and happily `delete` it as it wants to.
Georg Fritzsche
Yes, because to do what you suggest would require deliberate programming which would not be done, it is always possible to mess things up but a shared_ptr would stop it happening accidentally
Patrick
+6  A: 

You can make the destructor of that object private. That will trigger compile time error on attemp to delete object. Also you should allow restcted code to delete object by using friends mechanism or member function.

denisenkom
This is unfortunately undoable. I would have to make all correct users of the object friends (and they are <possibly> many). Also, due to how friendship and inheritance works, I'd have to do this in every derived object class.
laura
You can add a non-const method to the class which will delete the object using the private destructor. Anyone who can access the method can delete the object unless they got the const object in which case they can not call that method.
Komat
But then you'd have to give the classes const objects, which are not mutable.
laura
+6  A: 

I've traditionally seen these kind of scenarios implemented using a shared_ptr/weak_ptr combo. See here.

The owner/deleter would get a

boost::shared_ptr<T>

Your class would get a

boost::weak_ptr<T>

To reassign the weak ptr, simply reassign the pointer:

void MyClass::Reassign(boost::weak_ptr<T> tPtr)
{
    m_tPtr = tPtr;
}

To use the weak ptr, first check to see if it's still around:

void MyClass::Use()
{
    boost::shared_ptr<T> m_temporarySharedPtr = m_tPtr.lock();
    if (m_temporarySharedPtr)
    {
        //...
    }
}

The weak ptr can be made "NULL" by reseting it, or assigning it to an empty shared_ptr

void MyClass::MakeNull()
{
    m_tPtr.reset();
}
Doug T.
I think this might work and it looks simple enough, I need to look into the weak_ptr api to see exactly how to implement.
laura
I've accepted this answer: it was a though one between this and an own wrapper but there are times when it's better to trust well established libraries rather than write your own code.
laura
A: 

An alternative (if allowed by your restircitons) would be to create a dummy object similar to a shared pointer to act as a wrapper between the object in question and your classes.

Your classes can attempt to delete this object if they wish, but it itself will leave the original object untouched. Overload the * operator and you can use it transparently.

xan
Georg Fritzsche
Sometimes I hate C++... :(
xan
A: 

something like this?...

the Obj class is an aggregation of your new class, you point at it with an Obj* cont pObj, which you set up at the creation of your new class (or leave as 0 if it's not used), you then check pObj before calling any of its functions?

if ( pObj ){ pObj->foo(); }

if the function foo's incorrectly defined as mutable then you need to fix its declaration.

your new class isn't responsible for cleaning up/deleting the Obj class.

queBurro
and... make Obj's destructor private
queBurro
your Obj's not a singleton is it?
queBurro
+1  A: 

You can put a wrapper around the pointer to allow modification but not deletion:

template <typename T> class Wrapper
{
public:
    Wrapper(T *p=0) : pointer(p) {}

    T       *operator->()       {return pointer;}
    T const *operator->() const {return pointer;}
    operator bool()       const {return pointer;}

private:
    T *pointer;
};

You can use this just like a pointer to the template type in some contexts, but can't call delete on it. The wrapped type must be a struct or class type (i.e. a type where -> makes sense). Then one of your classes that uses, but doesn't manage the lifetime of, the object would look a bit like this:

class User
{
public:
    void Assign(Object *o) {object = o;}
    void UseObject() {if (object) object->Use();}

private:
    Wrapper<Object> object;
};

Technically, you can still get at the raw pointer, but the code to do it is very wrong-looking:

delete wrapper.operator->();
Mike Seymour
I like this idea, but need to see exactly what it involves. It seems easier to implement than the weak_ptr one, especially since I can guarantee non-destruction of the object from it's parent while my classes are working.
laura
I've added some clarifation.
Mike Seymour
shared_ptr and weak_ptr are nice things - if you can change all classes that use or manage the objects to use them.
Mike Seymour