0

From what I've read, a shared_ptr<T> does not get de-allocated until both strong references AND weak references to it are dropped.

I understand a shared object to be considered expired when there are no more strong references to it. The standard lock() function of a weak_ptr<T> therefore fails in such a case because the object is considered 'expired'.

However, if the deleter of a shared pointer is overridden such that the managed object is not deleted, then it should be valid to generated shared_ptr<T> from a weak_ptr<T> - but I cannot find the right syntax to do this.

std::shared_ptr<int> s_ptr(new(42), D());
std::weak_ptr<int) w_ptr(s_ptr);
s_ptr.reset();

s_ptr = std::shared_ptr<int>(w_ptr, false);

EDIT

To clarify this a bit further, I'm trying to construct an object pool of re-usable shared_ptr<T>. The reason behind this is because every use of shared_ptr results in one or more heap memory allocations. So I've added a deleter to every shared_ptr<T> which stores a weak_ptr<T> reference such that then the deleter gets called it should be able to re-add itself to a pool of available shared_ptr<T> objects (with managed object intact). By keeping a weak_ptr<T> stored inside the shared_ptr<T>'s deleter, it therefore shouldn't stop the deleter from being called.

The end goal is to obtain a smart pointer that doesn't do consisant heap allocation - or at least only a small number.

Nicholas
  • 1,392
  • 16
  • 38
  • 1
    I don't think this kind of use is intended. Also, what makes you so sure that the pointer is preserved anywhere after it isn't needed any more? Just curious, what are you trying to solve with that? BTW: What exactly do you mean with your first sentence? What exactly is not being deallocated? It sounds ambiguous to me. – Ulrich Eckhardt Jul 18 '15 at 07:01
  • It sounds both pointless and dangerous to me, an XY problem. What is the actual objectives? – user207421 Jul 18 '15 at 07:10
  • @Ulrich - By the first sentence I mean than the control block still exists are the strong pointer count reaches zero. Ref: "The control block does not deallocate itself until the std::weak_ptr counter reaches zero as well." from http://en.cppreference.com/w/cpp/memory/shared_ptr – Nicholas Jul 18 '15 at 07:21
  • @EJP, I'm trying to construct an object pool of shared_ptr. Not a pool of the managed objects, but a pool of shared_ptr's to the object. I am keeping a weak_ptr reference to any allocated shared_ptr to keep it alive for recycling. – Nicholas Jul 18 '15 at 07:23
  • I reason behind wanting an object pool of shared pointers is because I am creating a truck load of them - each existing for a short period of time. Therefore I was concerned about memory fragmentation. I figured creating an object pool of them should be easy... – Nicholas Jul 18 '15 at 07:29
  • 1
    Why doesn't the deleter return the object to the pool then? – Ulrich Eckhardt Jul 18 '15 at 07:30
  • @Ulrich, the deleter does return the object to the pool - or at least its trying to. The deleter function only gets a handle to the managed object itself - not the control block nor the shared_ptr itself. The deleter object cannot store a reference to the shared_ptr directly because that will result in a stronger reference and therefore the deleter will never be called. That is why I'm storing a weak_ptr inside the deleter. – Nicholas Jul 18 '15 at 08:19
  • The deleter is invoked to release the pointee from the ownership of last shared_ptr. Assuming the object was allocated with `new`, the default deleter thus uses `delete` to handle this. If you want, you can take the raw pointer (it ceased to be involved with any shared/weak pointer!) and store it in the pool again. How you store it inside the pool is up to you, I'd consider is using a `vector>`. When retrieving an element from the pool, you simply remove it from the vector and convert it to a `shared_ptr` with a proper deleter. Note that no reference is retained in the pool! – Ulrich Eckhardt Jul 18 '15 at 10:36
  • @UlrichEckhardt, Yes, storing the raw pointers to T is one way to do this. However doing that will require converting the raw `T` point into a `shared_ptr` when the user requests a new shared_ptr from the pool. Thus a new allocation on the heap for the control block. I was hoping that this control block allocation could be avoided. – Nicholas Jul 18 '15 at 12:15

2 Answers2

1

From what I've read, a shared_ptr does not get de-allocated until both strong references AND weak references to it are dropped.

Wrong. a std::shared_ptr has two blocks - a control block that contains reference counts, etc, then another block for the actual data.

When the shared count goes to 0, the data block is (usually) deallocated. This is why it is illegal to make a std::shared_ptr from a expired std::weak_ptr.

On another note, why would you EVER want this functionality? It ruins the entire point of std::weak_ptr, which is to be able "store" a pointer to a object stored by std::shared_ptr without incrementing its reference count. If you want to do that, then please just use a std::shared_ptr

Russell Greene
  • 2,141
  • 17
  • 29
  • I did mention that I was overriding the deleter function such that the managed object wasn't being deleted. The reason behind this usage is to pool the shared_ptrs for reuse. – Nicholas Jul 18 '15 at 07:16
  • true, sorry. Any reason you can't just use a contariner of `shared_ptr`s? – Russell Greene Jul 18 '15 at 07:23
  • A container of shared_ptr's will then keep a strong reference to the managed object, and thus the use-count will never reach zero - therefore the deleter will not fire. (I'm using the deleter function to do the actual returning of the shared_ptr to the object pool) – Nicholas Jul 18 '15 at 07:27
  • Because this is kinda a hack, i think that a `weak_ptr` wont do what you want because that is exactly what `weak_ptr` is trying to prevent. In order to do what you want, then I suggest you just store a raw pointer to the object. – Russell Greene Jul 18 '15 at 15:04
  • I absolutely agree with you - except for the fact that creating a shared_ptr from a raw pointer results in a heap allocation for the control block - which is exactly what I'm trying to avoid. – Nicholas Jul 18 '15 at 15:38
  • No, you could just cache one. use `make_shared` then use the `shared_ptr::get` function to store the pointer. – Russell Greene Jul 18 '15 at 20:45
1

If pooling control blocks is a good idea, the library implementation may already be doing it. In fact, the implementation of new itself may already be doing pooling to support similar memory usage patterns.

Furthermore, you can achieve what I think your goals are by using make_shared rather than invoking new and passing it into a shared_ptr constructor; one of the reasons for the existence of this helper function is that it can be written to use a single allocation to allocate both the control block and the new object you're creating at the same time.

  • The difficulty with using `make_shared` is that it doesn't allow for a custom deleter. I can find zero ways to use custom deleter logic with `make_shared`. Which means the managed object must be heap deleted. – Nicholas Jul 18 '15 at 09:05
  • Are you implying that one could construct and destruct shared_ptr's ad infinitum without concern of memory fragmentation because default memory allocator pool's control blocks constructed? – Nicholas Jul 18 '15 at 09:07