Re: an intrusive smart pointer
On Dec 28 2007, 8:36 pm, Mathieu Lacage
<mathieu.lac...@sophia.inria.fr> wrote:
I am using an adhoc intrusive templated smart pointer implementation and
[snip]
I really would like to support the direct new syntax:
Ptr<T> p = new T ();
A simple way to support this would be to initialize the refcount to zero
in T::T.
Yes, an unmanaged object should have refcount 0.
Think invariants.
However, when I attempted to do this, I found the following
issue. If you write:
T::T ()
{
DoSomething (this);}
void
DoSomething (Ptr<T> p)
{}
The call to DoSomething is going to increment and decrement the refcount
to one and back to zero, hence, triggering a delete before the object is
fully constructed.
So, I really wonder if it is possible to support both the direct new
syntax and calls to DoSomething from within the constructor.
Yes and no.
First, the "no". doSomething is by design requiring a fully
constructed T object that has already been placed under management of
a smart pointer instance, but is called with a pointer to a T object
that isn't fully constructed and isn't yet managed. That's a design
and/or coding error.
One cure for doSomething is to make sure that there's no implicit
conversion from T* to Ptr<T>: make that constructor explicit.
Now if you really want to call doSomething from a T constructor, e.g.
as the very last action, you can do
struct WithUppedRefCount // friend of T
{
T* p;
WithUppedRefCount( T& o ) p( &o ) { p->addRef(); }
~WithUppedRefCount() { p->nonDestructiveRelease(); }
operator T* () const { return p; }
};
T::T()
{
// ...
assert( this->refCount() == 0 );
WithUppedRefCount safeSelf( this );
{
doSomething( Ptr<T>( safeSelf ) );
}
}
However, note that this is medicine for a symptom, it's not a cure.
A cure would be to redesign doSomething so that it doesn't require a
managed object. Also, it would probably be a good idea to make sure
that no raw T* pointer could ever be held by accident by client code.
One way to ensure that is to define a placement operator new to
obfuscate new-expressions, and provide a general macro NEW that
supplies the hairy non-intuitive arguments and produces a Ptr<T>.
Just document the usage, e.g.
Ptr<T> p = NEW( T,( blah, blah, blah ) );
In C++0x it's possible that one may dispense with that evil macro
stuff and use language supported argument forwarding instead.
Cheers, & hth.,
- Alf
Disclaimer: written somewhen late after new year's eve.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]