3

Question: Are there any compelling reasons to use naked pointers for non-owning resources or should we be using weak_ptr?

CPP.reference states

std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else

But then, in the accepted answer for Which kind of pointer do I use when? we have the statement:

Use dumb pointers (raw pointers) or references for non-owning references to resources and when you know that the resource will outlive the referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.... If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a shared_ptr and use a weak_ptr.

This answer is followed by a lot of back-and-forth about the use of naked pointers, with no real resolution. I can't see any reason for using dumb pointers. Am I missing something?

Community
  • 1
  • 1
rsjaffe
  • 5,600
  • 7
  • 27
  • 39
  • 1
    Shared and weak pointers are expensive. Don't use them unless you have an actual use case that requires them. – Kerrek SB Mar 22 '16 at 23:18
  • A `weak_ptr` has to refer to an object "managed" by shared pointers. A dumb pointer doesn't have to. It can point to anything. – juanchopanza Mar 22 '16 at 23:19
  • 3
    In pure C++, you generally *do not* want raw pointers, unless you're one of the 0.1% who really can't take the small performance impact. This is entirely subjective, but the majority are in the 'always use smart pointers' camp; just to explain the back-and-forth. – Collin Dauphinee Mar 22 '16 at 23:20
  • The first alternative to raw pointers worth consideration is typically std::unique_ptr, rather than a weak pointer. You only need to buy into shared and weak pointers if the lifetime of the managed object is more complicated than a single intra-function scope or object lifetime. – Tony Delroy Mar 22 '16 at 23:28
  • The future [`observer_ptr`](http://en.cppreference.com/w/cpp/experimental/observer_ptr) is what will probably solve most of the issues above. – vsoftco Mar 22 '16 at 23:29
  • @Collin Well. There are many cases for *non-owning* pointers. There’s no smart pointer in the standard library that matches this mould (and really, what would it even be smart about?), so a raw pointer will do. – Konrad Rudolph Mar 22 '16 at 23:34
  • Those two quotes do not conflict. We should be using *raw pointers* when we know the managing *smart pointer* will outlive the *raw pointer*. When we do not, then *shared/weak* pointer is appropriate. One thing to consider is we should not make unnecessary demands on what type of *smart pointer* can call down into our functions. If we use a `weak_ptr` we make it impossible for *unique_ptr* (for example) to use our functions. There is *nothing wrong* with using *raw pointers*. – Galik Mar 22 '16 at 23:35
  • This answer may be of interest, it also references the CppCoreGuidelines written by Bjarne Straustrup and Herb Sutter: https://stackoverflow.com/questions/35543520/how-to-pass-shared-ptr-to-class-with-lower-lifetime/35543963#35543963 including rule [R.30](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r30-take-smart-pointers-as-parameters-only-to-explicitly-express-lifetime-semantics) – Galik Mar 22 '16 at 23:50
  • Interesting [R.30](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r30-take-smart-pointers-as-parameters-only-to-explicitly-express-lifetime-semantics): "A function that does not manipulate lifetime should take raw pointers or references instead." But what if the function needs to know about whether an object is alive or not? A raw pointer won't cover that. – rsjaffe Mar 23 '16 at 01:49
  • Looked at `observer_ptr`. Once concern with that is that, when using an object that you have a pointer to, you assume the object will not be deleted in the midst of a call to an object method. So in a multithreaded application, the overhead of the `weak_ptr` may be worth it to avoid using an object while it is being deleted. – rsjaffe Mar 23 '16 at 02:26
  • @RoryJaffe If the function needs to know if the pointer is alive or not then `weak_ptr` is an excellent choice. Raw pointers are for when you know the lifetime of the smart pointer will outlive the raw pointer. – Galik Mar 23 '16 at 12:44

2 Answers2

6

A weak_ptr has a very specific purpose: to break shared_ptr cycles. As an example, std::enable_shared_from_this is based on letting an object contain a weak_ptr to itself. If it directly contained a shared_ptr then that would create a cycle, so instead it has a weak_ptr.

You use a weak_ptr where you would otherwise have had a shared_ptr. The weak_ptr has a higher cost because in addition to the costs of shared_ptr there is the object existence checking that produces a shared_ptr, or not. Also it's a more complex beast so that it's easier to use incorrectly.

I can't think of any way that weak_ptr has anything to do with “temporary ownership” (except that after checking of existence and using a produced shared_ptr, that's one temporary shared ownership, which is repeated again and again for each use). For example, std::enable_shared_from_this has nothing to do with temporary ownership. Ordinarily I would just ignore a source of such a claim, and advice others to ignore it, but cppreference.com is about the best we have in the way of a free online C++ reference. It's odd that it contains a nonsense statement. But, nothing's prefect, as I once remarked to Bjarne in clc++, whereupon he corrected my speling of “prefect”. Hm! Well.


I don't know of any advantage in using a raw pointer rather than a weak_ptr, where a weak_ptr is what's required. A raw pointer can't do a weak_ptr's job of holding on to a shared_ptr's control block, so it seems to me that the mere idea of replacing a weak_ptr with a raw pointer is nonsense. But then, the day one stops being surprised by learning things one could never have thought of, is the day one is dead, and so it may be the case that there is some obscure-to-me such use.


Also, there is no advantage in using a weak_ptr where a raw pointer is what's required. Rather, the weak_ptr introduces a relatively speaking enormous cost, of control block allocation and reference counting. So I can't think of any situation where it would make sense to replace raw pointers with weak_ptr, and I don't expect this to be a case where I learn otherwise.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    Sorry to tell you, if you want to have a pointer, that is not owning, you should use raw pointers unless you need the a weak_ptr, because of the added overhead. – Arne Mar 23 '16 at 02:36
  • 1
    @Arne: Well I would agree. No need to be sorry. ;-) – Cheers and hth. - Alf Mar 23 '16 at 02:39
  • According to the c++ conference videos I saw, you should use unique_ptr for all owning pointers, raw pointers for all non owning pointers, shared_ptr only if you really have a shared ownership eg: an object shared between two threads, and weak_ptr if you explicitly allow your object to die between each access. Throwing shared pointers at everything is concidered as bad coding style. – Arne Mar 23 '16 at 13:21
  • https://github.com/martinmoene/observer-ptr has a discussion about `observer_ptr` and the associated code. With raw pointers, the pointer has no idea whether the object still exists, and in a multithreaded application, it's hard to guarantee that an object is completely destroyed in order. I can *initiate* destruction in order, but it appears that some things hang in there longer than others (e.g., signal queues). Having the dependent objects be smarter about whether pieces of the system still work seems to be the way to protect against this issue. Seems like that's a role for `observer_ptr`. – rsjaffe Mar 23 '16 at 14:18
  • @Arne: Regarding "weak_ptr if you explicitly allow your object to die between each access", that's doesn't cover `std::enable_shared_from_this`, because the object containing the weak pointer cannot die before that weak pointer is used. So let's say the video you saw is, at the least, an over-simplification. More generally, rely on common sense, not someone's silly mechanical rules. – Cheers and hth. - Alf Mar 23 '16 at 18:43
  • Ok, I never used enable_shared_from_this nor can I imagine where it really is useful, because despite the fact that I can create a valid shared_ptr from a non owning raw pointer, I still can't ask weather the raw pointer is still valid, (or can I? I didn't write a test for that). The only usage in weak_ptr that I know is an object cache. – Arne Mar 23 '16 at 22:48
  • @Arne: No, you can't ask whether the raw pointer is valid. Mostly `enable_shared_from_this` is used where you have a raw pointer or reference that you know is valid, e.g. passed in as function argument, and you want to store a `shared_ptr`. In particular this often happens when legacy code is converted to use smart pointers. – Cheers and hth. - Alf Mar 23 '16 at 22:53
  • ok I don't really work with legacy code, so there might the experience bias come from. – Arne Mar 23 '16 at 23:04
2

a weak pointer actually has very little usage. A weak pointer is only useful, if you need it's lock function (transform the pointer into a shared pointer, to prevent garbage collection while you are operating on it). Non owning pointers best, if you just use raw pointer, because of the overhead shared and weak pointer have.

Arne
  • 7,921
  • 9
  • 48
  • 66
  • When would you *not* want to prevent garbage collection while operating on an object that the pointer refers to? – rsjaffe Mar 23 '16 at 02:28
  • @RoryJaffe Most of the time. You should design your applications such that ownership and lifetime of resources is clear and well defined. In such a system it should be relatively rare for a pointer to need to check if it is still valid. – Galik Mar 23 '16 at 12:48
  • @RoryJaffe most of the time, when you are passing pointers down to functions, you have an owning pointer, like a shared pointer on the calling side which already prevents that the objects can be deleted within the callee context. – Arne Mar 23 '16 at 12:54