21

shared_ptr is to be used when we have a scenario where it is desirable to have multiple owners of a dynamically allocated item.

Problem is, I can't imagine any scenario where we require multiple owners. Every use-case I can image can be solved with a unique_ptr.

Could someone provide a real life use-case example with code where shared_ptr is required (and by required, I mean the optimal choice as a smart pointer)? And by "real life" I mean some practical and pragmatic use-case, not something overly abstract and fictitious.

code
  • 1,056
  • 10
  • 22
  • 1
    Search the web. There are countless "real life" programs, with public source code using `shared_ptr`. – Drew Dormann Feb 16 '18 at 20:23
  • 5
    I already searched, long and hard. While there are countless examples of how to use shared_ptr, I have yet to see practical use-case examples for it. Most of the time, it is used when it shouldn't be (this includes most examples on web). – code Feb 16 '18 at 20:23
  • 1
    We have a framework here which routes messages to multiple threads according to "topics" which the corresponding components can register listeners for. In many cases, it makes sense for there to be multiple components listening to the same topic, so we use a `shared_ptr` to allow the receivers to share memory, then the message gets freed once the last receiver is done processing it. – Daniel Schepler Feb 16 '18 at 20:25
  • 5
    _"Every use-case of having multiple owners of a dynamically allocated item I can image can be solved with a unique_ptr."_ I'm curious as to how you are managing to imagine having multiple owners of a unique_ptr. – Eljay Feb 16 '18 at 20:28
  • 1
    Eljay, I don't. I meant that most of the time when you think you need multiple owners, you don't in fact need multiple owners. Well, at any rate, this is the question to be answered. We'll see what code examples people can up with. I asked in an exploratory sense. I'm not preaching or stating anything. Just explaining my current point of view and asking for clarification. – code Feb 16 '18 at 20:31
  • 1
    In a graphics engine, it could make sense for multiple on-screen objects to share the same resources (3D model data, textures, etc.) So in a cache of resources loaded from disk, it would make sense to use shared pointers so that resources can't get expired from the cache until all scene objects requiring the resources have been removed. – Daniel Schepler Feb 16 '18 at 20:35
  • 2
    Eljay, also, to be fair, you have in quotes (quoting me) saying something I didn't say. I said, "Every use-case I can image can be solved with a unique_ptr." That is not the same as what you quoted. No big deal, I hope my question and motives are a bit more clear. – code Feb 16 '18 at 20:35
  • 1
    Questions of the form "show me some examples..." are not appropriate here. There is no _correct_ answer. [Here is but one example](https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WComboBox.html) of a "real world" program that has objects with multiple owners. – Drew Dormann Feb 16 '18 at 20:36
  • 8
    I vote for reopen because this question is rather direct and clear: give a valid example of code where use of `shared_ptr` would be the only acceptable solution. I personally can not came up with such a code example. Note that something like "there are gorrillion of real world programs (and even programming languages) built around shared ownership of everything" is not really an answer. – user7860670 Feb 16 '18 at 20:40
  • @code • I tried to put square brackets around the portion that I had copied from your first sentence into the other sentence to provide in context, but that got interpreted funny by stack overflow's parser. If that's not what you had actually meant in your question, you should revise your question, since that is how it reads. – Eljay Feb 16 '18 at 20:44
  • In my project, we use smart pointers (not std::shared_ptr yet, but we're converting to that) in order to have shared ownership of large, deep objects that are expensive to copy. Their use is pretty much for performance. – Eljay Feb 16 '18 at 20:46
  • Thank you VTT. I also feel it is very clear and to the point. It is not easy to come with such code. If someone could provide a code example, we could understand shared_ptr better (from a software engineering perspective, that is). – code Feb 16 '18 at 20:48
  • Eljay, that sounds like a bad plan. You should be using unique_ptr as a default and not shared_ptr. shared_ptr is for a very niche case, very rarely needed. So rare in fact, that not many would be able to illustrate its need with a code example. This is an advanced question for software engineers with deep understanding of software design. – code Feb 16 '18 at 20:52
  • Daniel, in the graphics engine example, you could use a unique_ptr and pass raw pointer for usage to the functions and threads since they are lower on call stack, guaranteed the resource would not be destroyed. The listener example is interesting, but I'm not sure without code example if shared_ptr is necessary. – code Feb 16 '18 at 21:04
  • @VTT I don't know if reopening is the correct path to take. I agree with you that the question is direct and clear. However, [it is chatty, open ended, there is no actual problem, and every answer is equally valid](https://stackoverflow.com/help/dont-ask) – Drew Dormann Feb 16 '18 at 21:32
  • Drew, respecftully I don't agree that it is open ended. Over 4 years of C++ confrences there is yet to be a proper example of when shared_ptr is "needed". Sutter, et all, have made example after example about how unique_ptr suffices. There is a correct answer, a clrea example of when it is needed via code example. It illustrates both how it is used and why it is needed. The first to answer with clear code example wins. I'm not asking to debate what is the best IDE or plain text editor. This question in my opinion is not open ended at all. – code Feb 16 '18 at 21:40
  • Drew, I'm not asking for an infinite number of examples, just one. Not the best example. Just an example that is clear and illustrates actual "NEED" for shared ownership of a dynamic object. If you re-open it, you will see what a struggle it will be to provide such an example. it would be a huge asset to people to have this answered. – code Feb 16 '18 at 21:40
  • Also, just because some project uses shared_ptr...that does not mean it is necessary for them to do so. So saying this project or that project uses it, is pointless. We need a clear code example so we can examine if truly there is a case where there is a "NEED" for shared ownership. shared_ptr can be used for any dynamic allocation... and as such is over used. The key word here is "need". It is not easy to come up with a code example to illustrate the "need". – code Feb 16 '18 at 21:44
  • @code, I’ve developed a CAD app and I use shared_ptr for the reason Daniel suggested, to save RAM and VRAM when multiple models have a same mesh. “you could use a unique_ptr” doing so is likely to introduce following classes of bugs. 1. Memory leaks when you forget to delete a mesh from the scene when the last model using it is deleted 2. Use after free when you delete a mesh that’s still used my some model 3. Concurrency bugs when you manually deleting a mesh while some function still uses that in another thread (when used correctly, both shared_ptr and weak_ptr are thread safe). – Soonts Feb 16 '18 at 21:49
  • Soonts, thanks. I have not worked on a CAD app so I'm not familiar with all the intricacies of such an app on the rendering end. I imagine that if you made a unique_ptr to an array of contiguous data and you subdived the processing of that data to pass the work seqments to threads, you could pass a raw pointer to those threads. Naturally you would have to use 1 of many syncro schemes avail, but if the code is blocking until threads return, there would be no issue with memory leaks or early deletions (I imagine). I'm stating this as an ex discussion point only, not as any sort of critique. – code Feb 16 '18 at 21:59
  • `shared_ptr` has type-erased deleter, which might be useful for type erasure in general. Also, what about copy-on-write? – Daniel Langr Feb 16 '18 at 22:01
  • @code • _"Eljay, that sounds like a bad plan."_ **shrug** It's an old and very large code base. _"This is an advanced question for software engineers with deep understanding of software design."_ I suppose my project just doesn't have software engineers of that eminent caliber, myself included. – Eljay Feb 16 '18 at 22:01
  • Soonts, I guess it all depends on how the workers are spin off and how the work manager class is designed. You wouldn't need the entire app to blocking until worker threads return, just the work manager class. Again, I'm just theorizing here as a discussion point. I don't claim any wisdom here :) – code Feb 16 '18 at 22:02
  • Eljay, that is not what I meant. It just sounded like you were going to switch over to shared_ptr as defult from what ever you used before. Just a miss understanding. – code Feb 16 '18 at 22:04
  • @code no, not to continuous data, meshes are more complex than that. Here’s an example for ya: https://gist.github.com/Const-me/0e18d895481a7c0556c102f6bff973f0 – Soonts Feb 16 '18 at 22:25
  • 2
    Languages like C# and Java, where all objects are by reference (and collected by the garbage collected), effectively is as if everything was analogous to C++ std::shared_ptr. For any of those use case scenarios where shared (GC'd) references are suitable, is also the situation where shared_ptr in C++ could be considered. – Eljay Feb 16 '18 at 23:27
  • One case I had was a factory producing singletons by type which were then shared by several other objects, and when all the other objects were destroyed (which would happen at varia asynchronous events), the singleton was released. The factory used a weak pointer to know when the singleton had been destroyed so if another object asked for a singleton of that type it could create it again and share it as before. The factory could have a unique ptr on to the singleton, but then you would need a mechanism to tell the factory that it can be released... – Ian4264 Feb 16 '18 at 23:34
  • @Ian4264 Sounds very similar to the case of a graphics engine holding a cache of loaded resources, except in that case the factory is creating singletons indexed by a resource name or ID. (And in the case of a cache, you can optionally have the cache itself hold a shared_ptr to prevent things from being unloaded until it actually needs to evict least-recently-used resources from a full cache. But there, you might be getting into territory where it would make sense to create a wrapper around `shared_ptr` and `weak_ptr` to clarify the intended use and semantics.) – Daniel Schepler Feb 17 '18 at 00:54
  • A possible (not very satisfying) answer raised by a comment below: Interfacing with legacy pre-C++11 code which requires input to be copyable, even though it doesn't actually store multiple copies in the long run and redesigned code would probably use move operations instead. – Daniel Schepler Feb 17 '18 at 01:38
  • 1
    I'm not going to bother finding a public real-world example for you as this is off-topic, and I cannot legally give you any of the many examples from my own codebase, so I'll just write a comment to re-affirm to you that `shared_ptr` absolutely does have real, non-contrived, appropriate uses, and leave it at that... except to then also echo previous comments that you can research these on your own - you say you've done this and rejected all examples you've found. Well, I say either you need to keep looking, or you're falsely rejecting valid applications (more likely). – Lightness Races in Orbit Feb 17 '18 at 01:42
  • 1
    _"it would be a huge asset to people to have this answered"_ Not really – Lightness Races in Orbit Feb 17 '18 at 01:44
  • 1
    I _will_ tend to agree that there are vastly more examples of appropriate `unique_ptr` than of appropriate `shared_ptr`, but that's fine – Lightness Races in Orbit Feb 17 '18 at 01:46
  • 1
    `std::promise` and `std::future` are typically implemented using a `shared_ptr` to manage the shared state. – T.C. Feb 17 '18 at 11:01

9 Answers9

19

In our simulator product, we use a framework to deliver messages between simulation components (called endpoints). These endpoints could reside on multiple threads within a process, or even on multiple machines in a simulation cluster with messages routed through a mesh of RDMA or TCP connections. The API looks roughly like:

class Endpoint {
public:
    // Fill in sender address, etc., in msg, then send it to all
    // subscribers on topic.
    void send(std::unique_ptr<Message> msg, TopicId topic);

    // Register this endpoint as a subscriber to topic, with handler
    // called on receiving messages on that topic.
    void subscribe(TopicId topic,
        std::function<void(std::shared_ptr<const Message>)> handler);
};

In general, once the sender endpoint has executed send, it does not need to wait for any response from any receiver. So, if we were to try to keep a single owner throughout the message routing process, it would not make sense to keep ownership in the caller of send or otherwise, send would have to wait until all receivers are done processing the message, which would introduce an unnecessary round trip delay. On the other hand, if multiple receivers are subscribed to the topic, it also wouldn't make sense to assign unique ownership to any single one of them, as we don't know which one of them needs the message the longest. That would leave the routing infrastructure itself as the unique owner; but again, in that case, then the routing infrastructure would have to wait for all receivers to be done, while the infrastructure could have numerous messages to deliver to multiple threads, and it also wants to be able to pass off the message to receivers and be able to go to the next message to be delivered. Another alternative would be to keep a set of unique pointers to messages sent waiting for threads to process them, and have the receivers notify the message router when they're done; but that would also introduce unnecessary overhead.

On the other hand, by using shared_ptr here, once the routing infrastructure is done delivering messages to incoming queues of the endpoints, it can then release ownership to be shared between the various receivers. Then, the thread-safe reference counting ensures that the Message gets freed once all the receivers are done processing it. And in the case that there are subscribers on remote machines, the serialization and transmission component could be another shared owner of the message while it's doing its work; then, on the receiving machine(s), the receiving and deserialization component can pass off ownership of the Message copy it creates to shared ownership of the receivers on that machine.

Daniel Schepler
  • 3,043
  • 14
  • 20
  • 1
    Hmmm, I think this is a really good example. It's the asynchronicity that is the driving force behind the need (at least in this example). Or in other words, it is what makes a single owner impractical. Great, thanks. – code Feb 17 '18 at 14:21
  • 3
    Life is given to something in one place but that place has no use for that object, its purpose is merely to give life to the object and then to pass it onto functions that independently will make actual use of it in an asynchronous way, unrelated to each other. I'll let others chime in, but I think this is a great example. – code Feb 17 '18 at 14:32
4

In a CAD app, I use shared_ptr to save RAM and VRAM when multiple models happen to have a same mesh (e.g. after user copy-pasted these models). As a bonus, multiple threads can access meshes at the same time, because both shared_ptr and weak_ptr are thread safe when used correctly.

Below’s a trivial example. The real code is way more complex due to numerous reasons (GPU buffers, mouse picking, background processing triggered by some user input, and many others) but I hope that’s enough to give you an idea where shared_ptr is justified.

// Can be hundreds of megabytes in these vectors
class Mesh
{
    std::string name;
    std::vector<Vector3> vertices;
    std::vector<std::array<uint32_t, 3>> indices;
    BoundingBox bbox;
};

// Just 72 or 80 bytes, very cheap to copy.
// Can e.g. pass copies to another thread for background processing.
// A scene owns a collection of these things.
class Model
{
    std::shared_ptr<Mesh> mesh;
    Matrix transform;
};
Soonts
  • 20,079
  • 9
  • 57
  • 130
  • 1
    Thanks Soonts, but honestly I don't get it. I don't see how you're doing the passing nor do I see multiple consumers with justified ownership. I think it is important to make the point that just because multiple items may make use of a mesh it doesn't mean they need to own it. The mesh can be owned by 1 unique_ptr and the unique_ptr can be owned by a work management class that feeds and syncs threads, and feeds them with new meshes when ready. Those threads working on the mesh data don't actually need to own it to make use of it. – code Feb 17 '18 at 04:00
  • 1
    You can pass the threads in the work management class a raw ptr to the mesh via unique_ptr s get(). With that design with unique_ptr your code would be just as flexible, and trivially faster and memory leaner. So...just as fast and memory lean. While I don't think share_ptr is wrong to use here, I don't think this illustrates an actual "need" to use it. Of course, shared_ptr can be used for any use-case. You don't need to own what you use. – code Feb 17 '18 at 04:03
  • 1
    While shared_ptr works here, if I understood your code right this is classic example of when not to use shared_ptr. Since your workers only process data (mesh), they don't need to own the mesh. They only need a raw pointer from get() method of unique_ptr. The mesh should be owned by a unique_ptr which in turn is owned higher up in the call stack by something like a mesh work management class or something tat feeds the threaded workers with a raw pointer. I don't see the need for ownership transfer or sharing. – code Feb 17 '18 at 05:21
  • 1
    I think people often end up using shared_ptr because they did not place the smart pointer that owns high enough in the call stack. They expect ref counting of shared_ptr to save them from bad code design, which it often does. Not saying that is the case here. Actually I'm not sure how you intent to pass things around since that is left out of code example. – code Feb 17 '18 at 05:28
  • @code So your suggestion is to add one more superobject that handles all the data? Moreover, in architecture you propose the objects are actually shared with the superobject handling the reference count and garbage collection, in place of shared_ptr's standard counter. – bipll Feb 17 '18 at 08:06
  • @code The work + resource management class you’re talking about will be quite complex. Because multithreading, that’ll easily take a couple of days to correctly implement + debug. When a standard library feature saves that amount of time, that would be my definition of “requirement”. – Soonts Feb 17 '18 at 10:26
  • About multithreading, it doesn’t matter how high on the stack the owner is. The problem solved by shared_ptr, user deletes a model while some background thread processes that mesh. You delete the mesh on the GUI thread (where you got user input events) while the background thread accesses that mesh, the background thread will crash trying to access freed memory. You don’t delete the mesh, you leak memory. Good luck solving the problem with raw or unique pointers. – Soonts Feb 17 '18 at 10:27
  • The exact message passing / work scheduling infrastructure is not important, anything will do. In my CAD app that part is not even in C++. – Soonts Feb 17 '18 at 10:28
  • bipli, no you don't need to do that. You are assuming that if something needs axs for usage it needs a smart pointer to it. A raw pointer will do. There is no need for ref counting at all. The work manager class can have smart pointer that manages life of mesh. You need to manage the work and threads regardless. You don't get to skip that with any scheme. So there is nothing extra required actually. – code Feb 17 '18 at 13:50
  • Snoonts, if user wants to delete a mesh as you say in GUI, they are really just stating their intent to delete. The intent is handled by functions in any scheme. So in that case, the work manager manages the threads and is the one to delete the mesh once appropriate. Clean and concise. Lettting everything delete a mesh all over the code base is a mess. If you write your entire code base with such spaghetti actions no one new on the team will ever figure out design of the code as understanding all the states that could happen and who can do what becomes nightmare to maintain understanting – code Feb 17 '18 at 13:57
  • Soonts, also while the work manager is involved, it is nothing that an intermediate programmer can't do in a day. Further, you have to do that work with any scheme anyway, since you need code to manage and feed the threads. It is not like that code is optional. Threads don't just read your mind and grab work and sync work on their own. Someonehas to write that code. This is not extra something. All I'm saying is that since you need that anyway, it can be the one to own the mesh and also the one to del it. – code Feb 17 '18 at 14:01
  • Soonts, everyone else only need access to the mesh, which simply means a raw pointer extracted from unique_ptr. you don't need to ref count anything. – code Feb 17 '18 at 14:02
  • Soonts, to be clear, I'm not saying you can't use shared_ptr here, just that you don't actually need to. And using unique_ptr actually as far as I can see, involves no real extra work at all. But that's just my take on the issue. The unique_ptr case is optimal, lean, and clean but requires forethought in dessign. You'd have to model your classes for it. With shared_ptr, your design can be more open eneded. Prob why so pop, you can plug in shared_ptr to existing code rather than re-design code & work flow. – code Feb 17 '18 at 14:38
  • Soonts, your need for shared_ptr comes from your code design, not from your use-cases. Your use-cases could have been solved efficiently via unique_ptr with diff code dessign with a centralized work manager. This is not to say that what you did is wrong, just that it doesn't demonstrate a clear and succinct need for shared_ptr. it does not make it clear what are the characteristics of a use-case where shared_ptr (or some other ref counted ptr) is required. – code Feb 17 '18 at 14:44
  • @code “the work manager manages the threads” threads are managed way outside C++. I only have performance-critical and rendering-related stuff in C++. The rest of the app is implemented in safer and higher-level language. “and is the one to delete the mesh once appropriate” and how would it find out when that is? Keep in mind it could be more than one background tasks accessing the mesh, and potentially a task can take very long time to complete. – Soonts Feb 17 '18 at 18:24
  • “Lettting everything delete a mesh all over the code base” not everyone, just user. Not all over the code, just on the GUI thread, in the user input event handlers. You might not like the design, but the majority of GUI apps have similar one. – Soonts Feb 17 '18 at 18:25
2

In my program's user interface, I have the concept of "control point values" (a control point value represents the current state of a control on the hardware my program controls), and (of course) the concept of "widgets" (a widget is a GUI component that renders the current state of a control point to the monitor, for the user to see and/or manipulate).

Since it is a pretty elaborate system that it needs to control, we have

  • lots of different types of control point values (floats, ints, strings, booleans, binary blobs, etc)
  • lots of different types of widget (text displays, faders, meters, knobs, buttons, etc)
  • lots of different ways that a given widget could choose to render a particular control point value as text (upper case, lower case, more or fewer digits of precision, etc)

If we just did the obvious thing and wrote a new subclass every time we needed a new combination of the above, we'd end up with a geometric explosion of thousands of subclasses, and therefore a very large codebase that would be difficult to understand or maintain.

To avoid that, I separate out the knowledge of "how to translate a control point value into human-readable text in some particular way" into its own separate immutable object that can be used by anyone to do that translation, e.g.

// My abstract interface
class IControlPointToTextObject
{
public:
   virtual std::string getTextForControlPoint(const ControlPoint & cp) const = 0;
};

// An example implementation
class RenderFloatingPointValueAsPercentage : public IControlPointToTextObject
{
public:
   RenderFloatingPointValueAsPercentage(int precision) : m_precision(precision)
   {
      // empty
   }

   virtual std::string getTextForControlPoint(const ControlPoint & cp) const = 0
   {
      // code to create and return a percentage with (m_precision) digits after the decimal point goes here....
   }

private:
   const int m_precision;
};

... so far, so good; now e.g. when I want a text widget to display a control point value as a percentage with 3 digits of after the decimal point, I can do it like this:

TextWidget * myTextWidget = new TextWidget;
myTextWidget->setTextRenderer(std::unique_ptr<IControlPointToTextObject>(new RenderFloatingPointValueAsPercentage(3)));

... and I get what I want. But my GUIs can get rather elaborate, and they might have a large number (thousands) of widgets, and with the above approach I would have to create a separate RenderFloatingPointValueAsPercentage object for each widget, even though most of the RenderFloatingPointValueAsPercentage objects will end up being identical to each other. That's kind of wasteful, so I change my widget classes to accept a std::shared_ptr instead, and now I can do this:

std::shared_ptr<IControlPointToTextObject> threeDigitRenderer = std::make_shared<RenderFloatingPointValueAsPercentage>(3);

myWidget1->setTextRenderer(threeDigitRenderer);
myWidget2->setTextRenderer(threeDigitRenderer);
myWidget3->setTextRenderer(threeDigitRenderer);
[...]

No worries about object lifetimes, no dangling pointers, no memory leaks, no unnecessary creation of duplicate renderer objects. C'est bon :)

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
2

Suppose I want to implement a GLR parser for a language that is or contains a recursive "expression" definition. And the parsing must not just check whether the input conforms to the grammar, but also output something that can be used to do analysis, evaluations, compilations, etc. I'll need something to represent the result of each expression or subexpression grammar symbol. The actual semantic meaning of each grammar rule can be represented by polymorphism, so this will need to be some sort of pointer to a base class Expression.

The natural representation is then a std::shared_ptr<Expression>. An Expression object can be a subexpression of another compound Expression, in which case the compound Expression is the owner of the subexpression. Or an Expression object can be owned by the parse stack of the algorithm in progress, for a grammar production that has not yet been combined with other pieces. But not really both at the same time. If I were writing a LALR parser, I could probably do with std::unique_ptr<Expression>, transferring the subexpressions from the parse stack to the compound expression constructors as each grammar symbol is reduced.

The specific need for shared_ptr comes up with the GLR algorithm. At certain points, when there is more than one possible parse for the input scanned so far, the algorithm will duplicate the parse stack in order to try out tentative parses of each possibility. And as the tentative parsings proceed, each possiblity may need to use up some of those intermediate results from its own parse stack to form subexpressions of some compound expression, so now we might have the same Expression being used by both some number of parse stacks and some number of different compound Expression objects. Hopefully all but one tentative parsing will eventually fail, which means the failed parse stacks get discarded. The Expression objects directly and indirectly contained by discarded parse stacks should possibly be destroyed at that time, but some of them may be used directly or indirectly by other parse stacks.

It would be possible to do all this with just std::unique_ptr, but quite a bit more complicated. You could do a deep clone whenever parse stacks need to split, but that could be wasteful. You could have them owned by some other master container and have the parse stacks and/or compound expressions just use dumb pointers to them, but knowing when to clean them up would be difficult (and possibly end up essentially duplicating a simplified implementation of std::shared_ptr). I think std::shared_ptr is the clear winner here.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I was thinking of mentioning a similar example, where you want to implement binary search trees but with the ability to take snapshots, then make small changes. Then it would make sense to have the two trees share pointers to common (unmodified) subtrees, though the modified parts and their parents would of course need to be duplicated. Pretty much the same idea. (And very similar to what typically happens in functional programming languages like OCaml or Haskell, when you implement immutable data types and then an insert operation would return a new tree with large parts shared from input.) – Daniel Schepler Feb 17 '18 at 01:33
1

Take any lambda, called within a member function, f, of a class, C, where you want to deal with an object that you would pass into the lambda [&] as a reference. While you are waiting inside f for the lambda to finish, C goes out of scope. The function is gone and you have a dangling reference. Segmentation fault is as close as you get to defined behavior, when the lambda is next accessing the reference. You cannot pass the unique punter into the lambda. You couldn't access it from f once it's moved. The solution: shared pointer and [=]. I code the core of a database. We need shared pointers all the time in a multi-threaded infrastructure. Don't forget about the atomic reference counter. But your general scepticism is appreciated. Shared punters are used nearly always when one doesn't need them.

Kaveh Vahedipour
  • 3,412
  • 1
  • 14
  • 22
  • I'm not sure if this is actually an answer: with C++14, you can provide initializers to captures and thus use e.g. `[the_unique_ptr = std::move(the_unique_ptr)] { }` to pass ownership to the lambda. (That said, I *have* used `shared_ptr` in passing lambdas to Boost ASIO, as that requires a copyable handler, but it could be argued that's a shortcoming of the ASIO library which IIRC was originally written pre C++11.) – Daniel Schepler Feb 16 '18 at 23:30
  • Yes, you can pass ownership down as Daniel stated. Although I don't think that is necessary. Without code I can't be totally clear what you have in mind, but due to how call stacking works, I don't actually think it is possible for a class to go out of scope whose member function is waiting for some other function down the call stack. That I'm pretty sure is guaranteed not to happen. Perhaps I'm mistaken about what code design you have in mind. – code Feb 17 '18 at 03:31
  • At level 1 you have class C. class C has f1(). At level 1, f1() is executed. f1() calls f(2). At level 2 (one level down in call stack) f(2) is working. C is at level 1 and can not go out of scope until f2() is finished and then f1() is finished. Only then can level 1 code progress. Now if f(2) is asyncronous and f1() does not wait for it, the data you are passing to f2() should not be from f1() or C. In my opinion that is a design issue. Of course, this is just my opinion. I think there are num. ways to solve this without shared ownership (so there is no "need" for it). – code Feb 17 '18 at 03:42
  • ...just to add unless the data is just going through C or F1() as a ref or ptr. and is managed by a smart pointer higher up. But that is just a given I think. – code Feb 17 '18 at 03:46
1

See this real life example. The current frame is shared across multiple consumers and with a smart pointer things get easy.

class frame { };

class consumer { public: virtual void draw(std::shared_ptr<frame>) = 0; };

class screen_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };
class matrox_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };
class decklink_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };

int main() {
    std::shared_ptr<frame> current_frame = std::make_shared<frame>();

    std::shared_ptr<consumer> screen_consumer = std::make_shared<screen_consumer_t>();
    std::shared_ptr<consumer> matrox_consumer = std::make_shared<matrox_consumer_t>();
    std::shared_ptr<consumer> decklink_consumer = std::make_shared<decklink_consumer_t>();

    std::vector<consumer> consumers;
    consumers.push_back(screen_consumer);
    consumers.push_back(matrox_consumer);
    consumers.push_back(decklink_consumer);

    //screen_consumer->draw(current_frame);
    //matrox_consumer->draw(current_frame);
    //decklink_consumer->draw(current_frame);

    for(auto c: consumers) c->draw(current_frame);


}

Edited:

Another example can be a Minimax tree, to avoid cyclic redundancy weak_ptr in conjunction with shared_ptr can be used:

struct node_t
{
    std::unique_ptr<board_t> board_;
    std::weak_ptr<node_t> parent_;
    std::vector<std::shared_ptr<node_t>> children_;
};
seccpur
  • 4,996
  • 2
  • 13
  • 21
  • 1
    I don't really see the use of using pointers at all for `screen_consumer`, etc. instead of just `screen_consumer_t screen_consumer;` . Also, it's not clear from this example why `virtual void draw(frame&) = 0;` would not be sufficient. I think any answer will have to involve a real transfer of ownership to a pool of owners with no clear "head" - for example, if the `screen_consumer_t` et al were actually running in separate threads, and there's also a producer thread that doesn't want to wait for the consumers to be done. – Daniel Schepler Feb 17 '18 at 01:16
  • @DanielSchepler: IMHO multiple consumers need to be stacked in an STL container so that they can be processed together, so pointer should come easy too. – seccpur Feb 17 '18 at 01:26
  • Personally, I agree with Daniel. You are just putting specialized consumers into a vector and then calling their specialized draw. There is no sharing of ownership required here. You could have used unique_ptr instead. You are only "using" these specialized consumers. There is no shared ownership required. Polymorphism still would work. – code Feb 17 '18 at 03:23
0

Have you checked these articles about copy-on-write vector:

https://iheartcoding.net/blog/2016/07/11/copy-on-write-vector-in-c/

copy-on-write PIMPL:

https://crazycpp.wordpress.com/2014/09/13/pimplcow/

and generic copy-on-write pointer:

https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-on-write

All of them use shared_ptr internally.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
0

std::shared_ptr is an implementation of reference counting technique in C++. For use-cases of reference counting see linked wikipedia article. One usage of reference counting is garbage collection in programming languages. So if you decide to write a new programming language with garbage collection in C++ you can implement it with std::shared_ptr, though you will also have to deal with cycles.

ks1322
  • 33,961
  • 14
  • 109
  • 164
0

Simply put: there isn't really any.

For more detailed explanation, let's turn to formal reasoning. As we all know, C++ is a Turing-complete deterministic language. A popular simple example of equally computationally powerful tool is Brainfuck (often very convenient in establishing Turing-completeness of your favorite language of choice). If we look into Brainfuck's description (which is very small indeed, which makes it very handy for the purposes noted heretofore), we'll soon find out that there is not a single notion of anything resembling shared_ptr in there. So the answer is: no, there is no a real-life example where they would be absolutely required. Everything computable can be done without shared_ptrs.

If we continue the process thoroughly, we'll get rid equally easily of other unnecessary concepts, i.e. unique_ptr, std::unordered_map, exceptions, range-loops and so forth.

bipll
  • 11,747
  • 1
  • 18
  • 32