The idea is to have a unique_ptr
style owning pointer and the ability to hand out non-owning pointers that know when the object gets deleted. This smart pointer should be treated as an addition to unique_ptr
and shared_ptr
, as it's covering the gray area between those two and has it's unique areas of application.
I haven't investigated the performance yet, but I assume due to reference counting and managing the validity of the pointer it will be slightly slower than unique_ptr
and shared_ptr
. The only exception is accessing the pointer, which shouldn't have any overhead.
Basic usage:
owner<int> ownerOfInt = make_owner<int>( 4 );
weak<int> weakToInt = ownerOfInt.get_non_owner();
assert( weakToInt.ptr_is_valid() );
ownerOfInt.destroy();
asser( !weakToInt.ptr_is_valid() );
GameObject/World example:
class World {
…
weak<GameObject> add( owner<GameObject> );
}
Class Renderer {
…
void observe( weak<GameObject> );
void render();
}
...
World world = World();
Renderer renderer = Renderer();
weak<GameObject> gameObject = world.add_gameobject( make_owner<GameObject>() );
renderer.observer( gameObject );
while (1) {
world.update(); // gameObject could be deleted here
renderer.render(); // but renderer will notice and no longer access the ptr
}
...
enable_weak_from_this:
class MyClass : public enable_weak_from_this<MyClass> {
void someFunc(OtherClass& o) {
o.set_parent( this.get_non_owner() ); // Requires MyClass to be wrapped by owner<> or it returns a weak<nullptr>
}
}
I use this smartpointer for example in a case where I have a World
that owns multiple GameObject
and a Renderer
. GameObject
and World
knows nothing about Renderer
and Renderer
doesn't really care about World
, how long the GameObject
lives, if it gets replaced, or even deleted. All it cares about is that if it observes a GameObject
that its valid and accessable.
- We get 100% control over object lifetime and ownership, for the price of no threadssafety because an object can be deleted at any time by its owner.
- We get assurance that we only access valid objects without needing their ownership, for the price of some memory and performance overhead.
Why not use std::shared_ptr<>
and std::weak_ptr<>
?
- With
shared_ptr<>
andweak_ptr<>
as soon as you give out one of both to code you don't control, you have no control over the object lifetime anymore. If that external code decides to turn theweak_ptr
into ashared_ptr
, that external code also from now on decides when the object gets deleted. Withowner<>
andweak<>
the code that actually owns the object decides when it gets deleted.
Why do I get memory leaks when using owner/weak with maps?
- Use
map.emplace()
(C++11) to insert objects into the map - Make sure your object has a destructor defined, for example in inheritance don't forget to declare virtual destructors (
virtual ~MyClass() = default;
is enough)
How can I get weak<>
from this
?
- The class need to inheret from
enable_weak_from_this<T>
withT
being the class itself to get the protected methodget_non_owner()
. Be aware that this method only returns a validweak<>
if the object's lifetime is managed byowner<>
.
Why does this.get_non_owner()
result in a weak<>
nullptr?
- This happens if the object's lifetime is not managed by
owner<>
and/or the object's class doesn't inherit fromenable_weak_from_this<>
.
How can I have multiple owner<>
in a vector with different pointer types?
- Use
owner_t
orweak_t
, for examplevector<owner_t>
orvector<weak_t>
.
- provide custom deleter
- implicit conversion from unique_ptr
- non_owner class, that does nothing except flagging that the pointer is not owned.