Converting my program to smart pointer and a bit confused

Started by
11 comments, last by Washu 9 years, 3 months ago

I've been working on some game engine for some weeks using raw pointer and references and i decided to migrate to smart pointer before the project gets too advanced. To be honest i was fine with raw pointer but i came across a library i definitely want to use (Cereal) and somehow it won't be possible if i don't update myself and start using those smart pointer...

Anyway i am somewhat confused how smart pointer can totally replace references in my software.

Basicly i have a Scene class that contain gameentities. I have references in my game entity that point to the scene wich they belong so i can access public method of my scene from any entity in that scene.

I think this is a fairly normal thing to do and so far i was very satisfied with that. However i don't get how smart pointer can take over the references here. Basicly from what i've seen i can't use smart pointer to point to any variable on the stack without risking corrupting my memory and the scenes happens to be declared on the stack (in a vector inside the gamestate class)

So what i am supposed to do? Is everything supposed to be declared on the heap now that i want to use smart pointer? How about that vector of scene ? Is everything that i want to ever reference elsewhere now is supposed to be constructed from a smart pointer because somehow is it now impossible to point to something that wasn't a smart pointer in the first place so every container now have to be container of smart pointer instead of just being vector of object?

Any tips would be greatly appreciated.

Advertisement
I'm unfamiliar with the library in question and I'm not sure what kind of shared pointer you are referring to. I assume std::shared_ptr?

First, your objects are already on the heap since they are inside an std::vector. Second, although they are on the heap they are owned by the vector and you cannot just delete them yourself (either with delete or by falsely transferring ownership into a smart pointer).

You can, however, construct an std::shared_ptr which will not try to free the memory it 'owns', for example with
mySharedPointer.reset(myRawPointer, [] (MyRawPointerType*) { });
While this can be useful in isolated circumstances using this at large usually means you are doing something wrong. Either you are not putting your objects where you are supposed to put them or the library which requires that of you is not well designed.
Smart pointers only express ownership.

If you do not own (or jointly own) a resource, you should not be using a smart pointer and should instead be using raw pointers/references.

The above does assume that the non-owning pointers/references do not last longer then the resource they point at. If you need to safely be able to point at a resource without owning it then you can use std::weak_ptr which must be put into a std::shared_ptr before it can be used (and will return nullptr if the resource has been deleted).

Here's a quick rundown of the differences:
  • std::unique_ptr: I am the only owner and the resource will go away when I say it will. This is the cheapest type of owning smart pointer. If you are making one of these (and have a C++14 compiler) you should use std::make_unique() to avoid leaks in the face of exceptions.
  • std::shared_ptr: I am one of several owners - or a owner of a resource that others want to hold weak_ptrs to. The resource will go away when no one cares about it anymore. This is more expensive then a unique_ptr, and you have to be careful with circular references. If you are making one of these, you should use std::make_shared() to avoid leaks in the face of exceptions.
  • std::weak_ptr: I do not own this resource, but I may stick around longer then the resource I'm pointing at. This lets you safely point at a shared_ptr without actually forcing it to stay around. Most commonly used to break circular references, but there are other uses.
  • Raw pointer: I do not own this resource, and the resource must stick around longer then I do.
  • Raw reference: I do not own this resource, and the resource must stick around longer then I do. It also cannot be nullptr (or reassigned).
In most cases you will see the various *_ptr classes inside other classes as member variables, and only rarely as function parameters (usually because that function is handing ownership over to someone else). You will also see them as return values.

Raw pointers and references are more commonly used for function parameters since most functions don't need to transfer or hold ownership for their parameters - simply operating on them. In these cases the caller is responsible for ensuring the resource doesn't die out from under them.

think this is a fairly normal thing to do and so far i was very satisfied with that. However i don't get how smart pointer can take over the references here. Basicly from what i've seen i can't use smart pointer to point to any variable on the stack without risking corrupting my memory and the scenes happens to be declared on the stack (in a vector inside the gamestate class)

So what i am supposed to do? Is everything supposed to be declared on the heap now that i want to use smart pointer? How about that vector of scene ? Is everything that i want to ever reference elsewhere now is supposed to be constructed from a smart pointer because somehow is it now impossible to point to something that wasn't a smart pointer in the first place so every container now have to be container of smart pointer instead of just being vector of object?

Basically you make your vector a vector of smart pointers. The smart pointers will contain your objects, and the vector will contain smart pointers.

This requires alot of code rewriting though since everywhere you allocate an object will be replaced with allocating a shared pointer (which in the constructor you allocate your object).

I tend to use them in multithreaded code where code ran by one thread accesses objects created and destroyed by another. Since it only creates and destroys shared_ptr's now, and the shared_ptr use by the thread accessing it is actually a seperate object linked to the first shared_ptr, either thread can access or destroy their shared_ptr with total disregard for each other. The shared_ptr objects have a mutex and a reference counter so the last one left knows its time to delete the protected object.

(Yes, I know I still have to wrap the internal data members with a mutex when accessing it from many threads :p )


I think this is a fairly normal thing to do and so far i was very satisfied with that. However i don't get how smart pointer can take over the references here. Basicly from what i've seen i can't use smart pointer to point to any variable on the stack without risking corrupting my memory and the scenes happens to be declared on the stack (in a vector inside the gamestate class)

I think smart pointers are meant to help manage (long-live) heap objects.

If the variables are on stack why bother with smart pointer? Would reference be a better choice?

In your case, maybe you can reference to the vector that contains the scenes.

(1) You should always prefer references over pointers, so converting existing code that uses references to use pointers is a step backwards. If an API requires pointers to be passed in (and many of them do), just pass in pointers to your object (or pointer to your object references). If an API passed back pointers (ie. controls object lifetimes), treat them as if they were new/delete or open/close (see below).

(2) Your first step in using smart pointers is to locate any new/delete (or open/close, or create/release) pairs and replace them with std::unique_ptr and then use that class's get() function to obtain the raw pointer, and continue to use the raw pointer where you did before. You can, for example, cache all your textures in a vector of unique_ptrs and dole them out to consumers without them being any the wiser.

If your new and delete are not in symmetric pairs and the lifetime control of dynamically allocated objects is not straightforward, your design is broken and needs to be fixed first.

(3) Once in a rare blue moon you will run into situations in which std::unique_ptr is not going to do the trick. That's when std::shared_ptr (and its cracker cousin std::weak_ptr) come in to play. If it's the tool you need, reach for it, but it should not be your tool of first choice.

Stephen M. Webb
Professional Free Software Developer

(2) Your first step in using smart pointers is to locate any new/delete (or open/close, or create/release) pairs and replace them with std::unique_ptr and then use that class's get() function to obtain the raw pointer, and continue to use the raw pointer where you did before. You can, for example, cache all your textures in a vector of unique_ptrs and dole them out to consumers without them being any the wiser.

If your new and delete are not in symmetric pairs and the lifetime control of dynamically allocated objects is not straightforward, your design is broken and needs to be fixed first.


Just make sure your newly-created unique_ptr lasts long enough. You don't want to make a unique_ptr on the stack and then return its contents, for example...

Unfortunately the *_ptr classes don't work very cleanly with non-pointer objects (like file handles, for example), but there is at least one proposal for C++17 to add support for generic resource management classes that don't require pointers. Or you can quite easily make your own - the implementation of unique_ptr is especially easy smile.png
Having the public interface of the scene made available to anything that owns a scene entity seems suspect to me. Why do you need to access the scene in all these unrelated places?

I use scene entities but their owner has no access to the scene, just to the properties of the entity.

Bit off topic. Sorry.

Having the public interface of the scene made available to anything that owns a scene entity seems suspect to me. Why do you need to access the scene in all these unrelated places?

I use scene entities but their owner has no access to the scene, just to the properties of the entity.

Bit off topic. Sorry.

Probably for some kind of AI logic. Maybe the AI needs to go through the list of entities and react to them or something.

Just make sure your newly-created unique_ptr lasts long enough. You don't want to make a unique_ptr on the stack and then return its contents, for example...

Unfortunately the *_ptr classes don't work very cleanly with non-pointer objects (like file handles, for example), but there is at least one proposal for C++17 to add support for generic resource management classes that don't require pointers. Or you can quite easily make your own - the implementation of unique_ptr is especially easy smile.png


That's not precisely true. You can use them just fine in some cases, making only a few assumptions:
    // FILE * is the typical file handle used in C.
    std::unique_ptr<FILE, decltype(fclose)> fileHandle(fopen(/* stuff */), fclose);

    // HANDLE is a PVOID typedef most of the time in windows (and the chances of this changing are slim to none,
    // simply because of all the legacy code that makes the assumption that it is the size of a pointer (using void)
    // would also work.
    std::unique_ptr<typename std::remove_pointer<HANDLE>::type, decltype(CloseHandle)> fileHandle(CreateFile(/* stuff */), CloseHandle);

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement