4

Been trying to understand shared pointer for a few days now and it feels like I cant seem to get it. Not sure if it's just to obvious or if it's too complicated. First of all, could anyone please give me an example where you would ACTUALLY use shared pointers. The examples on Wikipedia makes no sense to me. And how would you pass a shared pointer to another function or create an object with a shared pointer. So, how do you pass it around and where would you use it? ANY information or examples would be great.

Also, I have this issue where I don't know what to use. I have this function where I allocate a QFile and passes it to a function in another class. That function takes the file as a QIODevice* and then creates an object containing the file. I was wondering what the best solution would be here and how (if I should) use a shared pointer here? How can I make a shared pointer with <QFile> and pass it in where the function takes <QIODevice>. Feels like I don't get shared pointers at all...

My other approach would be to put the allocation of the QFile in a QScopedPointer. I then pass it to the class and when creating the object where the file will be stored, I use QPointer or QScopedPointer. In the end of the first calling function I should call take() right?

function () {
   QScopedPointer<QFile> item(new QFile("filename"));
   SomeClassObject->doStuff(item.data());
   item.take();
}
---------------------------------
SomeClass::doStuff(QIODevice *item) {
    _currentObject = new MyObject(item); // should _currentObject be a smartpointer?
    ...
}
---------------------------------
class MyObject {
    QPointer<QIODevice> _item;

    ...
    MyObject(QIODevice *item) {  _item = item; }
}

So I want a way to store pointers and a way to handle them during creation if "new" throws an exception.

demonplus
  • 5,613
  • 12
  • 49
  • 68
chikuba
  • 4,229
  • 6
  • 43
  • 75

1 Answers1

10

The point of shared pointers (and other similar wrappers for pointers) is to handle destruction of the pointer-to object properly. That is instead of having to manually make sure you delete the last copy (and only the last copy), the shared pointer takes care of it for you when it goes out of scope. The shared part means that you can create copies of this wrapper object (the shared pointer object) and it will "share" the pointer-to object between the copies (just as if you made a copy of a regular pointer) with the added benefit described above.

As for your code, SomeClass::doStuff() should have a QScopedPointer<QFile> parameter (instead of a QIODevice* one) as you are passing item to it, which has that type.

Same with MyObject's constructor: have it take a parameter of QPointer<QIODevice> or QSharedPointer<QIODevice> type. In general, everywhere where you would use pointers, use QSharedPointer instead (with the appropriate template type). This will save you from headaches related to accessing deleted objects later on.

Of course, sometimes you actually need the raw QIODevice pointer (e.g. for third-party library call), then you would use the data() member function of the shared pointer object. Just make sure you do not persist (that is store or otherwise copy beyond what's necessary) the returned raw pointer, because that will undercut the purpose of the shared pointers -- the shared pointers will not know about your extra raw pointer that is not under the management of the shared pointer objects.

EDIT: take() releases the ownership of the pointed-to object from a scoped pointer, so when the scoped pointer is destroyed, it does not delete the object. You would ant ot use it in a situation when you transfered ownership to somthing else -- like in your case to MyObject.

Attila
  • 28,265
  • 3
  • 46
  • 55
  • 1
    I don't see a good reason to make the argument of the function a `QScopedPointer`. Either make it a *reference* (avoid pointers altogether), leave it as a raw pointer (allow *all* managed pointer types) or make it a shared pointer (avoid copying and make `_currentObject` share the object, if this is a possibility). Passing a scoped pointer would have to be by reference and then you force the choice of smart pointer on your users, which is not a nice thing to do (what if they are using a different type of smart pointer?) – David Rodríguez - dribeas Mar 23 '12 at 02:36
  • 1
    I do not see why passing the shared pointer (instead of the raw one) is a problem (besides the forcing of the particular type). I think it is safer than passing raw pointer (e.g. you can store the parameter to a global container without fear of the caller deleting it from under you). The shared pointer certainly doesw not have to be by reference, the copy-constructor logic will make sure the copy behaves appropriately. You could certainly pass the underlying object via reference, but then why use pointers at all? – Attila Mar 23 '12 at 02:45
  • it needs to take a QIODevice since I will not only use it with QFile. There are other situations in my code where functions need to take a basetype – chikuba Mar 23 '12 at 03:05
  • @chikuba - You can convert a `QSharedPointer` to a `QSharedPointer`, so you can use that type if you require the base type. – Attila Mar 23 '12 at 03:09
  • and the reason why take() is not in the doc for QSharedpointer is cus its part of QScopedPointer :) – chikuba Mar 23 '12 at 03:18
  • @DavidRodríguez-dribeas QFile and other QObjects must be allocated. and I would not pass it as a scoped pointer. need to change that. – chikuba Mar 23 '12 at 03:19
  • @chikuba - I see: `QSharedPointer` behaves as I described (sharing the pointer-to object), whereas `QScopedPointer` destroys the pointed-to object when it goes out of scope. I would prefer `QSharedPointer`, but it might have some slight performance penalty compared to `QScopedPointer`. If you use `QScopedPointer`, you will not be able to make copies of it (e.g. pass it as parameter). The purpose of `QScopedPointer` is to prevent memory leak in the face of exceptions, for example -- you do not have to manually hunt down all paths where the pointed-to object needs to be deleted. – Attila Mar 23 '12 at 03:33
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9220/discussion-between-chikuba-and-attila) – chikuba Mar 23 '12 at 03:42
  • 1
    but like, isnt the purpose of a shared pointer to use it when u have multiple owners? the object that i create in the end will be the owner of it, noone else. and cant i just used the scopedpointer when creating the object to handle exceptions, then use its raw pointer and then store it at the owner when everything is done? – chikuba Mar 23 '12 at 03:53
  • @Attila: The shared pointer does not *need* to be passed by reference, but you probably should. There is no need for the extra copy, and copying a shared pointer is not free (not expensive, but it involves atomic operations (or locking if the platform does not support it). If you prefer to hear it from someone with a greater experience, Herb Sutter points it out [here](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/C-11-VC-11-and-Beyond) (or maybe in the other talk he gave in Going Native 2012). – David Rodríguez - dribeas Mar 23 '12 at 11:13
  • @DavidRodríguez-dribeas - There is truth in that :) Good habit to learn. Thanks for the link. – Attila Mar 23 '12 at 14:26
  • " In general, everywhere where you would use pointers, use QSharedPointer instead" that's bad advice. smart pointers are for ownership. If you pass shared_ptr's around everywhere it is very hard to tell where ownership is retained if sometihng goes wrong. Also, I just ran a benchmark and the function call with a shared ptr vs raw ptr is about 20x slower (for a dummy function). You could pass a shared_ptr by reference, but why not just use a raw pointer? You know you won't delete it and it makes sure ownership isn't claimed where you don't expect it. – xaxxon Oct 12 '16 at 09:59