In what cases is this not possible? I model the resource as an object and that object has a memory location.
(Yes, in a non-deterministic GC, you can't do this for expensive resources, but that's a problem of GCs, not an in-principle problem).
EDIT: I am guessing the parent actually meant "associate the life time of a resource with the life time of a single, non-/never-shared storage location". The statement as written is one I have seen proponents of non-deterministic GCs make.
Something is handling both list [1], that something could own the object, or maybe something external to that. Giving ownership of the object to both list is a design error [2].
[1] e.g. if the two list are an implementation detail of a data structure, the data-structure itself could own the objects in the lists.
[2] Do non-deterministic garbage collectors that handle cycles allow you to have a resource with multiple owners? Yes. Should you do it? No, god, please don't.
Imagine you have an arbitrary long lived connected cyclic graph that can be incrementally updated from multiple short lived worker threads. With a GC, this is a no brainer: just put the objects in the graph. No workarounds, no extra tracking.
Without a GC, on removing a node, you have to walk to essentially do a mark/sweep of the graph to find dead nodes that were connected through the node you removed.
Removing an object is just as easy as removing an element from the vector. (If you test the weak_ptrs on use, that's actually the only thing you would need to do).
And I know it for the .NET GC, they tried a reference counting GC as alternative to a collecting GC and it performed worse and comes with the cycle trouble.
Reference counting works fine as long as you don't have cycles, of course.
Of course, not having to implement the garbage collector yourself is a benefit.
User decides to view a couple of orders. Orders reference products. Those products are managed by something (e.g. an unordered_map of shared_ptr). The order can check, is my product there? If so, i'll copy the shared_ptr to it. Otherwise, I create a shared_ptr for a product and store a copy in the manager. When the order's destructor is triggered, you check the count of the shared_ptr. If it is 2 (i.e. the order and the manager), the order removes the shared_ptr.
[1] Single ownership is the key concept, not reference counting which is just an implementation detail.
https://news.ycombinator.com/item?id=7315575
(Don't want to duplicate the comment once again.)
I don't think I asked for that but correct me if I did (maybe I'm just not understanding your point).
Anyways, could you elaborate why shared_ptr, unique_ptr, and weak_ptr don't work in that case?
shared_ptr works fine for my example. Shared immutable state is a case where reference counting works extremely well, since immutability implies no cycles.
Global variables exist and can be used to hold resources. If you have a resource that's not associated with any single function context you can move it into the global variable.
And reference counting also renders the point of determinism moot - now you never know if releasing a reference while trigger freeing a resource. You could of course delay offload it to a separate thread, but this makes you lose any guarantee about the time when the resource is actually freed, too.
You know that releasing the last reference will. That is why the important concept is single ownership of resources and not reference counting by itself.
And are generally considered bad practice.
Yep! Or, ideally, you use one of the good reference counting libraries that your language almost certainly ships with if it is one which encourages RAII.
I think the article is actually more of an argument against that line of reasoning than an argument about performance. Its main proposition is that properly used RAII gives you less opportunities for resource leaks of all sorts, and is very easy to do. I agree with you that it isn't wrong in all scenarios (I mostly program in Ruby and Javascript for goodness sake!), but I think a lot of people are unaware of some of the advances in patterns, libraries, and compilers that make it much easier to write software that doesn't need a GC.