Boost logo

Boost Users :

From: Kercso Jozsef (kercso.jozsef_at_[hidden])
Date: 2005-01-17 04:09:28


Hi!

   I would have several questions regarding to smart pointers.
They are completely new for me, and I'm not sure if my design
is the best. Feel free to comment it. :)

The "safe" pointer problem:
There is a class Data, whom instances are used by many other Processor
objects. It can happen, that a Manager object deletes the Data object,
so the Processors have invalid pointers. It must be made such a design,
where the Processors can check the Data pointers before using it,
if it is still valid or not.

My solution:
class Handler;

class Data {
     boost::shared_ptr< Data > m_sharedPtr; //shared pointer to myself
   public:
     Data();
     ~Data() {};
     Handler getHandler();
     int aMethod();
     ....
};

class Handler
{
     boost::weak_ptr< CTrain > m_weakPtr; //weak pointer to the handled Data
   public:
     Handler(boost::shared_ptr< Data > data)
     bool aMethod(int& result);
     ...
};

Data::Data() : m_sharedPtr(this) {}
Data::~Data() {}
Handler Data::getHandler() { return Handler(m_sharedPtr); }
int Data::aMethod() { return 1; }

Handler::Handler(boost::shared_ptr< Data > data) : m_weakPtr(train) {}
bool Handler::aMethod(int& result)
{
   bool retVal = !m_weakPtr.expired();
   if (retVal)
   {
     boost::shared_ptr< Data > p = m_weakPtr.lock();
     result = p->aMethod();
   }
   return retVal;
}

When somebody wants to access a Data object, it gets only
a Handler to it. The Handler can check with its weak pointer,
if the corresponding shared pointer is expired (data object deleted)
or not. Thus it can be assured, that no invalid pointer of the
Data object will be used by the Processors.
If the Manager object (who has a container of Data* ) deletes
a data object, the corresponding Handler gives false back,
because m_weakPtr.expired() returns false.

So everything seems to be OK, but there are several problems
and questions:
1. the destructor of the Data object will be called twice, when
    the Manager calls 'delete data;':
    - Once, when by the delete.
    - Once by the m_sharedPtr.
    After this the use_count() method returns -1 (!!!) and the
    m_weakPtr.expired() returns true (!!!). To eliminate this problem
    I initialize the m_sharedPtr in the Data constructor in the following
    way: m_sharedPtr(this, emptyDeleter), where emptyDeleter is an
    empty method: void emptyDeleter(Data* data) { }
2. What happens, if one of the Processors locks its m_weakPtr to
    obtain a shared pointer to acces the Data, but after obtaining the
    shared pointer the manager deletes the corresponding data object?
    If the Processor tries to acces again the (deleted) data object --> core dump.
    I have found the following solution to this problem:
    - before deletion the Manager somehow checks if data->m_sharedPtr.unique()
      returns true. If not, it refuses to delete the data object.
3. The Data object will be accessed many times by many Processors, and by
    every access the following construction will be used:
     if (!m_weakPtr.expired())
     {
       boost::shared_ptr< Data > p = m_weakPtr.lock();
       // do job on p
       ...
     }
    My question is: how "expensive" is such a construction? How
    fast is to obtain a shared pointer from a weak pointer unsing lock()?

If you already met the problem of these "safe" pointers and
have an other solution, please share it with us. Or if you have
a comment to my solution, then say it too. :)

Thanks,
   Jozsef Kercso


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net