Date: Thu, 13 Feb 2025 19:09:32 -0500
You could use shared_ptr and weak_ptr for that purpose (ie if you need to observe if a dynamically allocated object has been destroyed). But generally the easiest is to define object directly, ie without dynamic allocation. If this is not possible, because the most derived type is is not known at compile time, then using unique_ptr is easiest because it essentially behave the same as declaring the object directly. shared_ptr/weak_ptr are only necessary if an object may outlive the thread that allocated memory for it and you cannot predict, from source code structure, which thread is going to destroy the managed object.
The way unique_ptr is designed to work, it is not possible to define a "weak/observable" pointer interface to the memory held by that uniqe_ptr because whenever the unique_ptr gets destroyed, there is no object left for the "observable" pointers to refer to. For this, there would need to exist an intermediate object between the smart pointer and the managed object so as to signal that the managed object died.
But unique_ptr is intended to be an (as small as possible and zero overhead) RAII wrapper on a dynamically obtained memory. Hence it just holds a pointer to the managed dynamically allocated object (and also the deleter object, usually zero sized).
shared_ptr does have (and needs to have) an intermediate object called a control block, itself dynamically allocated, which holds the reference counts (both shared count and weak count). When the managed object dies, the control block itself does not get destroyed if the weak reference count is not zero. The control block stays allocated for as long as at least one weak_ptr still refers to that control block. Any such weak_ptr observes that the managed object died through the state of the associated control block (and thus would report a null value in that situation).
PS: An important remak is, a (non null) unique_ptr<T> object has exactly the same lifetime as declaring the T object naked. Pointers and refs get dangling when objects goes out of scope, and there's nothing peculiar about unique_ptr that would make it possible to enable observability of a dangling state. Compile time analysis of pointer lifetime would seem a more promising approach (I think there are active papers on this). There's generally no issue when passing around raw pointers and references to functions when you just need to access the object for reading or writing (and this is exactly what should be done to express these operations that do not operate on ownership). But you need to be careful whenever a class member function retains a copy of a raw pointer parameter into one of its data members (or a copy of the address of a reference parameter). Such a class must explicitly state it is intended to hold a non owning pointer, and its uses must ensure that the structure of code is such that the pointee is defined in a scope that wraps all uses of the object that refer to the pointee. Such design is not bad per-se, and there are legitimate use cases. Examples of such design with non-owning raw pointer members exists in std lib if you want to see actual use cases: basic_ios + basic_streambuf, poymorfic_allocator + memory_ressource, error_category + error_code.
On February 13, 2025 2:37:49 p.m. EST, JOHN MORRISON via Std-Proposals <std-proposals_at_[hidden]> wrote:
>A non-owning but self zeroing smart pointer for single ownership
>We need this when we want hold a persistent reference to an object already owned by a unique_ptr to use later if it is still valid. Not having it leaves us using raw pointers and we don't have a good art of preventing them from dangling (knowing if they are still valid). Our mitigations tend to be fragile and often break. It is scary enough to deter us from doing such a thing but sometimes we have to and it is here that dangling pointers remain a huge hazard.
>It doesn't have to be like that, We can have a smart pointer for the role but it will be involve intrusion on the owning unique_ptr declaration so it can notify deletes and some reference counting overhead but with that it is rock solid (valid or null). Here is a working example:
>https://github.com/make-cpp-nice/ptr_to_unique A smart pointer to an object already owned by a unique_ptr. It doesn't own the object but it self zeroes when the object is deleted so that it can never dangle.
>The addition of something like this to the Standard Library would complete safe smart pointer coverage for heap allocated objects. It is the missing piece needed to do this. Its safe encapsulation of an idiom that was unreasonably hazardous will also open up new design opportunities.
>
The way unique_ptr is designed to work, it is not possible to define a "weak/observable" pointer interface to the memory held by that uniqe_ptr because whenever the unique_ptr gets destroyed, there is no object left for the "observable" pointers to refer to. For this, there would need to exist an intermediate object between the smart pointer and the managed object so as to signal that the managed object died.
But unique_ptr is intended to be an (as small as possible and zero overhead) RAII wrapper on a dynamically obtained memory. Hence it just holds a pointer to the managed dynamically allocated object (and also the deleter object, usually zero sized).
shared_ptr does have (and needs to have) an intermediate object called a control block, itself dynamically allocated, which holds the reference counts (both shared count and weak count). When the managed object dies, the control block itself does not get destroyed if the weak reference count is not zero. The control block stays allocated for as long as at least one weak_ptr still refers to that control block. Any such weak_ptr observes that the managed object died through the state of the associated control block (and thus would report a null value in that situation).
PS: An important remak is, a (non null) unique_ptr<T> object has exactly the same lifetime as declaring the T object naked. Pointers and refs get dangling when objects goes out of scope, and there's nothing peculiar about unique_ptr that would make it possible to enable observability of a dangling state. Compile time analysis of pointer lifetime would seem a more promising approach (I think there are active papers on this). There's generally no issue when passing around raw pointers and references to functions when you just need to access the object for reading or writing (and this is exactly what should be done to express these operations that do not operate on ownership). But you need to be careful whenever a class member function retains a copy of a raw pointer parameter into one of its data members (or a copy of the address of a reference parameter). Such a class must explicitly state it is intended to hold a non owning pointer, and its uses must ensure that the structure of code is such that the pointee is defined in a scope that wraps all uses of the object that refer to the pointee. Such design is not bad per-se, and there are legitimate use cases. Examples of such design with non-owning raw pointer members exists in std lib if you want to see actual use cases: basic_ios + basic_streambuf, poymorfic_allocator + memory_ressource, error_category + error_code.
On February 13, 2025 2:37:49 p.m. EST, JOHN MORRISON via Std-Proposals <std-proposals_at_[hidden]> wrote:
>A non-owning but self zeroing smart pointer for single ownership
>We need this when we want hold a persistent reference to an object already owned by a unique_ptr to use later if it is still valid. Not having it leaves us using raw pointers and we don't have a good art of preventing them from dangling (knowing if they are still valid). Our mitigations tend to be fragile and often break. It is scary enough to deter us from doing such a thing but sometimes we have to and it is here that dangling pointers remain a huge hazard.
>It doesn't have to be like that, We can have a smart pointer for the role but it will be involve intrusion on the owning unique_ptr declaration so it can notify deletes and some reference counting overhead but with that it is rock solid (valid or null). Here is a working example:
>https://github.com/make-cpp-nice/ptr_to_unique A smart pointer to an object already owned by a unique_ptr. It doesn't own the object but it self zeroes when the object is deleted so that it can never dangle.
>The addition of something like this to the Standard Library would complete safe smart pointer coverage for heap allocated objects. It is the missing piece needed to do this. Its safe encapsulation of an idiom that was unreasonably hazardous will also open up new design opportunities.
>
Received on 2025-02-14 00:09:42