Question regarding returning an std::weak_ptr vs std::shared_ptr

So as the title suggests I was wondering about the different cases where it would be preferable to return a std::weak_ptr over a std::shared_ptr.

What brought this on was I am trying to do some updating to a entity system of mine and noticed another system like mine (Some article or book can't remember) that was returning a std::weak_ptr instead in the same situtation where I was returning a std::shared_ptr. I am curious as to why he/she would do this?

Here is my entity sysem below (The interface of it at least), basically it is a component based design and when requesting a component of that entity I am returning a std::shared_ptr that pointed to that component while the other person was returning a std::weak_ptr.

If you aren't familiar with component based design in games the components get requested all throughout the game for stuff like telling a entity to move, drawing a entity in the drawing code, any fucntionality regarding a entity basically you will need to grab a component for that functionality.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#ifndef ENTITY_HPP_1
#define ENTITY_HPP_1

#include <map>
#include <memory>
#include <SFML/System/Time.hpp>
#include <tinyxml2.h>
#include <easylogging++.h>
#include "EntityComponent.hpp"

class Entity
{
	public:
		typedef unsigned long IDType;
		typedef std::shared_ptr<Entity> StrongPtr;

		explicit Entity(Entity::IDType id);
		~Entity();

		void update(const sf::Time& deltaTime);

		bool init(tinyxml2::XMLElement* data);
		void postInit();
		void destroy();

		Entity::IDType getID() const { return m_ID; }

		// Templated method to retrieve components
		template <class ComponentType>
		std::shared_ptr<ComponentType> getComponent(EntityComponent::IDType id)
		{
			auto findIter = components.find(id);
			if (findIter != components.end())
			{
				EntityComponent::StrongPtr baseComponentPtr = findIter->second;
				std::shared_ptr<ComponentType> subComponentPtr = std::static_pointer_cast<ComponentType>(baseComponentPtr);

				return subComponentPtr;
			}
			else
			{
				// TODO: Figure out a better way to handle not finding a component
				// Right now we are just returning a null shared_ptr
				return std::shared_ptr<ComponentType>(nullptr);
			}
		}


	private:
		// This is called only by the EntityFactory. No one else should be adding components dynamically for now.
		void addComponent(EntityComponent::StrongPtr component);


	private:
		friend class EntityFactory;

		typedef std::map<EntityComponent::IDType, EntityComponent::StrongPtr> EntityComponents;
		EntityComponents m_Components;
		Entity::IDType m_ID;
};

#endif // !ENTITY_HPP_1 


What would be the reasoning behind having a std::weak_ptr returned for getComponent() instead of std::shared_ptr? Is it just to make it harder for the user to keep shared_ptrs or components lying around?
Last edited on
It's an ownership thing. The entity owns its components, so when the entity dies, so do all its components. Distributing weak_ptrs instead of shared_ptrs retains that ownership.

Whereas if it were handing out shared_ptrs, anything and everything that had a copy of the shared_ptr would be sharing ownership of the component, and the component would not die until all parties release their shared_ptr.
Last edited on
But wouldn't it still be possible to have shared_ptrs lying around even with returning weak_ptr? Granted it would take a extra step of casting it to shared_ptr which would help with them accidental times where you didn't mean to store it because of that extra step.

Though I'm wondering if that is worth having to cast every time you need to access a component, which is 99% of the time and you access a lot of components throughout a single frame.
Last edited on
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, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership. If the original std::shared_ptr is destroyed at this time, the object's lifetime is extended until the temporary std::shared_ptr is destroyed as well.
http://en.cppreference.com/w/cpp/memory/weak_ptr

The two canonical use-cases of std::weak_ptr are:

Break cycclic references between std::shared_ptr instances
http://msdn.microsoft.com/en-us/library/hh279672.aspx

Implementing an mru-cache of objects with a dynamic storage duration:
http://www.drdobbs.com/weak-pointers/184402026
Topic archived. No new replies allowed.