|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2008-07-15 12:28:33
On Jul 15, 2008, at 11:30 AM, Johan Torp wrote:
> Howard Hinnant wrote:
>>
>> However I did want to clarify one bit: sizeof(unique_ptr<int>) ==
>> sizeof(auto_ptr<int>) ==
>> sizeof(scoped_ptr<int>) == sizeof(int*). And unique_ptr<int> doesn't
>> allocate any extra memory on the heap (unlike shared_ptr, and just
>> like scoped_ptr and auto_ptr).
>>
>> The deleter of unique_ptr is a "static deleter". This is in contrast
>> to shared_ptr's "dynamic deleter". The static deleter requires no
>> overhead unless it contains state (the default_delete is stateless).
>>
>
> I didn't know this. I suppose this means that:
> 1. All code which potentially destroys a unique_ptr<T> needs to know
> the
> complete declaration of T
> 2. unique_ptr<T> can't be assigned to a unique_ptr<void>
>
> Correct?
Correct on both counts. Also see LWG issue 762 which is in Ready
status:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#762
which clarifies exactly which members of unique_ptr need T to be
complete and which don't. (anything that delete's the pointer needs
the complete type)
> Statement 1 can substantially increase compilation dependencies if
> everybody
> who passes around unique_pointers need to know the complete types even
> though they never use them. This type of code is extremely common. For
> instance, to expose dependencies you often have one instance of some
> class
> and pass it all over the application instead of having a singleton.
> Raw
> pointers, references and shared_ptr need not know the full type of the
> pointer value being passed which removes alot of include statement and
> thereby compilation dependencies. Can this problem be lessened by
> passing
> around r-value references?
I don't believe r-value references would help here. Note though that
as long as Base::~Base() is virtual, you can safely pass around a
unique_ptr<Base>(new Derived). Also one can wrap a unique_ptr up in a
pimpl pattern. As long as the pimpl's destructor is outlined, you can
hide the T pretty well.
Essentially the same rules which apply to auto_ptr apply to unique_ptr
(in this department) with one big exception: If you accidently
destruct an incomplete type, unique_ptr will complain at compile
time. auto_ptr just silently accepts this error. The design
philosophy of unique_ptr is: as much functionality as you can get
with no overhead. Let clients who need more functionality obtain it
by building their tools on top of unique_ptr (pay as you go) via
either wrappers or custom deleters.
> Statement 2 can be useful when you only care about objects'
> lifetimes and
> never expect to use it again. I find this pattern very useful,
> especially
> for storing a bunch of boost::signal::scoped_connection and other RAII
> objects which you just want to tie to some particular objects
> lifetime.
> Anyhow, I suppose unique_ptr<T> can be moved to shared_ptr<void>
> which has
> dynamic destruction (and can be type erased). Am I right?
Yes, unique_ptr<T> can be moved to shared_ptr<void>. That may or may
not be overkill for a given situation. I tend to prefer the zero-
overhead unique_ptr for RAII applications as often as I can get away
with it. Having the customizable deleter makes unique_ptr a lot more
flexible in the RAII department (compared to scoped/auto_ptr), but not
as flexible as shared_ptr.
The use of function-pointer-deleters allows for a nifty "poor-man's
dynamic deleter". A hybrid if you will between the typical unique_ptr
and shared_ptr use cases and overhead:
unique_ptr<int, void(*)(void*)> p1(std::malloc(sizeof(int)), std::free);
unique_ptr<FILE, int(*)(FILE*)> p2(std::fopen("test.dat", "w"),
std::fclose);
etc.
The function pointer may (for example) point into another dynamic
library. One might build a system whereby you "standardize" on
unique_ptr<void, void(*)(void*)> but construct the unique_ptr with
different function pointers (chosen at run time).
Of course since function pointers aren't stateless, you've swelled the
sizeof unique_ptr to two words instead of one. But there's still no
auxiliary heap access like you would get with shared_ptr.
Here's a little of the original motivation:
The subsection in there titled "Reference Deleter" has a nice RAII
demo where the type of the deleter is not known until instantiation
time and may be stateful (such as counting how many deallocations it
has made).
-Howard
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk