I'm somewhat used to the concept of refcounting through COM and I'm somewhat new to shared_ptr. There are several nice properties with CComPtr that I don't find in shared_ptr, and I'm wondering what are the pattern that prevent missuse of shared_ptr.
The AddRef/Release pattern guarantees there is only one refcount per object (the refcount is stored on the object itself), so it's safe, when you have a random pointer to create a CComPtr around it. On the other hand, shared_ptr has a separate refcount pointer, so it's unsafe to create a new shared_ptr on an object (why does the standard provide a constructor that takes a T* on shared_ptr if it's so unsafe to do?). That seems such a big limitation that I don't understand how one can use shared_ptrs...
A bit corner case: something that I've done in the past with AddRef/Release: I want a container of "weak references" to IFoos (for example a map from URL to IConnection or something). With weak_ptr, I can do that but my collection won't "clean itself up", I'll have expired pointers in it. With Release, I can implement my own weak pointer (a bit of work) that actually cleans up the collection. Is there an alternative with shared/weak_ptr?
Intuitively, there is a performance penalty in doing two memory allocations to create an object (one for the refcount, one for the object) compared to the IUnknown world where you do only one. There is also a locality penalty when accessing the object (assuming that an AddRef is frequently followed by reading the content of the object, which seems likely). Has the cost of both approaches been compared?
Because it's the only way to have
shared_ptr
s without being intrusive. You can use ashared_ptr
on anything. I've even used them on objects from C interfaces, via the use of a deleter object. Things like acairo_t*
and so forth. That way, I never have to free anything ever again.You can't do that with
CComPtr
; it only works forIUnknown
-style objects.Also, there is
std::make_shared
, which creates ashared_ptr
directly from an object type and the argument to the constructor. So you never even see the pointer (and it usually allocates the object and its ref-count in one allocation instead of two).The proper C++ idiom with
shared_ptr
is very simple: always usemake_shared
oralloc_shared
. If you can't use them, then the proper idiom is to only use the direct naked pointer constructor in tandem withnew
:shared_ptr<T> pVal{new T{...}};
(or the appropriate function that creates the pointer). Never use it on pointers that you don't know the origin of.No, but there are tools to make one if you so desire. Besides the obvious method (run through your collection periodically and remove dead
weak_ptr
s), you can associate a deleter with theshared_ptr
that will (in addition to deleting the pointer) call whatever cleanup function to remove thoseweak_ptr
s.See
make_shared
, above.You don't have to copy the
shared_ptr
to talk to its contents, nor do you have to bump the reference count to do so.Now, let's talk about some of the things
CComPtr
can't do. It's intrusive. It can't be used with arbitrary allocators or deleters (obviously not as important when it's intrusive). It can't do pointer aliasing, where you have ashared_ptr
to a member of an object, but the actual reference count is for the object it is a member of. That's a very useful thing to be able to do.Oh yeah, it's not cross-platform. It's not bound to COM,
IUnknown
, and all of that overhead.