I've been working with pointers for a few years now, but I only very recently decided to transition over to C++11's smart pointers (namely unique, shared, and weak). I've done a fair bit of research on them and these are the conclusions that I've drawn:
So with these tenets in mind, I set off to revise my code base to utilize our new shiny smart pointers, fully intending to clear to board of as many raw pointers as possible. I've become confused, however, as to how best take advantage of the C++11 smart pointers.
Let's assume, for instance, that we were designing a simple game. We decide that it is optimal to load a fictional Texture data type into a TextureManager class. These textures are complex and so it is not feasible to pass them around by value. Moreover, let us assume that game objects need specific textures depending on their object type (i.e. car, boat, etc).
Prior, I would have loaded the textures into a vector (or other container like unordered_map) and stored pointers to these textures within each respective game object, such that they could refer to them when they needed to be rendered. Let's assume the textures are guaranteed to outlive their pointers.
My question, then, is how to best utilize smart pointers in this situation. I see few options:
Store the textures directly in a container, then construct a unique_ptr in each game object.
class TextureManager { public: const Texture& texture(const std::string& key) const { return textures_.at(key); } private: std::unordered_map<std::string, Texture> textures_; }; class GameObject { public: void set_texture(const Texture& texture) { texture_ = std::unique_ptr<Texture>(new Texture(texture)); } private: std::unique_ptr<Texture> texture_; };
My understanding of this, however, is that a new texture would be copy-constructed from the passed reference, which would then be owned by the unique_ptr. This strikes me as highly undesirable, since I would have as many copies of the texture as game objects that use it -- defeating the point of pointers (no pun intended).
Store not the textures directly, but their shared pointers in a container. Use make_shared to initialize the shared pointers. Construct weak pointers in the game objects.
class TextureManager { public: const std::shared_ptr<Texture>& texture(const std::string& key) const { return textures_.at(key); } private: std::unordered_map<std::string, std::shared_ptr<Texture>> textures_; }; class GameObject { public: void set_texture(const std::shared_ptr<Texture>& texture) { texture_ = texture; } private: std::weak_ptr<Texture> texture_; };
Unlike the unique_ptr case, I won't have to copy-construct the textures themselves, but rendering the game objects is expensive since I would have to lock the weak_ptr each time (as complex as copy-constructing a new shared_ptr).
So to summarize, my understanding is such: if I were to use unique pointers, I would have to copy-construct the textures; alternatively, if I were to use shared and weak pointers, I would have to essentially copy-construct the shared pointers each time a game object is to be drawn.
I understand that smart pointers are inherently going to be more complex than raw pointers and so I'm bound to have to take a loss somewhere, but both of these costs seem higher than perhaps they should be.
Could anybody point me in the correct direction?
Sorry for the long read, and thanks for your time!
Introduction of Smart PointersC++11 comes up with its own mechanism that's Smart Pointer. When the object is destroyed it frees the memory as well. So, we don't need to delete it as Smart Pointer does will handle it. A Smart Pointer is a wrapper class over a pointer with an operator like * and -> overloaded.
Smart pointer that enforces unique ownership by transferring ownership on copy. Comparable to the deprecated std::auto_ptr Class. CHeapPtr Class. Smart pointer for objects that are allocated by using the C malloc function.
Why is auto_ptr deprecated? It takes ownership of the pointer in a way that no two pointers should contain the same object. Assignment transfers ownership and resets the rvalue auto pointer to a null pointer. Thus, they can't be used within STL containers due to the aforementioned inability to be copied.
A smart pointer is like a regular (typed) pointer, like "char*", except when the pointer itself goes out of scope then what it points to is deleted as well. You can use it like you would a regular pointer, by using "->", but not if you need an actual pointer to the data. For that, you can use "&*ptr".
Even in C++11, raw pointers are still perfectly valid as non-owning references to objects. In your case, you're saying "Let's assume the textures are guaranteed to outlive their pointers." Which means you're perfectly safe to use raw pointers to the textures in the game objects. Inside the texture manager, store the textures either automatically (in a container which guarantees constant location in memory), or in a container of unique_ptr
s.
If the outlive-the-pointer guarantee was not valid, it would make sense to store the textures in shared_ptr
in the manager and use either shared_ptr
s or weak_ptr
s in the game objects, depending on the ownership semantics of the game objects with regards to the textures. You could even reverse that - store shared_ptr
s in the objects and weak_ptr
s in the manager. That way, the manager would serve as a cache - if a texture is requested and its weak_ptr
is still valid, it will give out a copy of it. Otherwise, it will load the texture, give out a shared_ptr
and keep a weak_ptr
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With