0

I would like to use a pointer-like object

Ownership<Type> m_foo

for the owning object and handle

Reference<Type> m_someFoo

as a classical "pointer" in another context, whereas my Reference should know when the original object does not exist anymore (e.g. by returning nullptr) and it should furthermore be possible to prevent the original object from deletion for a small period of time (locking).

I know that shared_ptr (Ownership) and weak_ptr (Reference) provide similar functionality. However, locking a weak_ptr and accessing the raw ptr involves creation of a shared_ptr which is rather slow. Also I cannot decide not to lock the weak_ptr before accessing the raw ptr (e.g. while knowing that the object is not being deleted right now).

Is it wise to implement Ownership and Reference as follows:

  • Ownership knows addresses of its References (e.g. std::list<Reference<Type>*>)
  • When the owned object decays, these References are set to nullptr
  • Reference contains ptr to an uint m_lockCount in Ownership
  • upon locking, m_lockCount++, upon unlocking m_lockCount--
  • Ownership can't release object when m_lockCount != 0

This solution would be especially feasible for few Reference instances and high access rates through References.

markusneg
  • 3
  • 1
  • 1
    " involves creation of a shared_ptr which is rather slow". Do you know why it's slow? When the owned object decays, these References are set to nullptr, reference contains ptr to an uint m_lockCount in Ownership, upon locking, m_lockCount++, upon unlocking m_lockCount--, and Ownership can't release object when m_lockCount != 0. Also, I bet it's a whole lot faster than you seem to think. – Mooing Duck Aug 04 '16 at 21:05
  • 5
    Everything you have described is exactly what `shared_ptr` and `weak_ptr` are designed to handle for you. Use them as-is, don't try to replicate them manually. Locking a `weak_ptr` does create a new `shared_ptr`, but that creation is fast as it shares the data pointer from the original `share_ptr` incrementing its refcount until the copied `shared_ptr` goes out of scope. The whole point of refcounting is fast copies and not releasing owned objects when locks are active. – Remy Lebeau Aug 04 '16 at 21:16
  • 2
    Don't re-implement the wheel. – Jonathan Potter Aug 04 '16 at 21:29
  • 1
    And if you do re-implement the wheel, make sure you profile to make sure your improvement really is an improvement and test the bejeeebes out of it to make sure that slowdown you avoided doesn't do something important in an edge case you didn't consider – user4581301 Aug 04 '16 at 21:45
  • Naming a container for a pointer `Reference`? Are you the guy who chose `std::move` for something that simply casts to a prvalue? – kfsone Aug 04 '16 at 22:46
  • @Duck: shared and weak_ptr work by reference counting, not by setting the weak_ptr to null actively, do they? – markusneg Aug 06 '16 at 17:17
  • @Remy: weak_ptr has to check expired() before dereferencing and furthermore a new shared_ptr has to be created, leading to quite some speed loss [see e.g. here](http://stackoverflow.com/questions/35134635/why-calling-via-weak-ptr-is-so-slow?noredirect=1&lq=1). In certain cases, a more direct access via the "Reference" type could be desirable. @ kfsone: Reference imho is a good word for something referencing another object (of course, there are still the C++ references). – markusneg Aug 06 '16 at 17:31

1 Answers1

0

Your proposed alternative would be much slower than shared_ptr/weak_ptr simply due to the idea of using a std::list for back pointers to the references. If you only work in single threaded mode that would the only addition overhead. Add in threading and you need a lock to manipulate your reference count and reference list atomically (though once you have a list, you don't need the count). shared_ptr\weak_ptr adds a couple of pointers overhead to your object, and has no dynamic allocations past the initial one.

nate
  • 1,771
  • 12
  • 17
  • Sure, refcounting (see shared/weak_ptr) is fastest when creating and copying Ownerships and References a lot. However, the upper approach is faster when dereferencing References often as it behaves like a pointer. You're right with the lock, I would need it for the list. – markusneg Aug 06 '16 at 17:37
  • If your `Reference` classes prevent `Ownership` classes from freeing the underlying pointer, how is that different from everyone using `shared_ptr`s? If that is not true, then you must manipulate lock counts whenever you deference your `Reference`, at which point it is no different than `weak_ptr` – nate Aug 06 '16 at 18:08
  • Reference only prevents the object from decaying when the lock is active. I can still decide to directly access the ptr in Reference (maybe not threadsafe but fast) or lock by conditional increment operation (threadsafe lock count increment). The latter should still be faster than creating a weak_ptr from a shared_ptr (of course this should be tested). – markusneg Aug 08 '16 at 08:54
  • From what I can tell, and I think the link about the performance back this up, the only cost of constructing a `shared_ptr` from a `weak_ptr` is the atomic operations used to pin the object into memory. I don't see how you can do better than that, as you will end up with more or less the same logic. If you have an external method to prove that a particular access is safe, I agree you can squeeze a little performance out of that one use case, but I would question if you are optimizing the right thing. – nate Aug 08 '16 at 16:31