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_ptrinto 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>withTbeing 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_torweak_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.