Serenity Operating System
at master 157 lines 7.5 kB view raw view rendered
1# SerenityOS smart pointers 2 3---- 4## Introduction 5 6There are three main C++ smart pointer types used in SerenityOS. Each type describes the ownership (or lack thereof) of the pointee. 7 8The reason for using these pointers is to make it explicit through code who owns which resources, and how ownership is transferred. They also serve as a guard against memory leaks and use-after-free bugs. 9 10 11---- 12## OwnPtr\<T\> and NonnullOwnPtr\<T\> 13 14`OwnPtr` is used for single-owner objects. An object held in an `OwnPtr` is owned by that `OwnPtr`, and not by anybody else. 15 16This means that the `OwnPtr` is responsible for deleting the pointee when the `OwnPtr` goes out of scope. 17 18These pointers cannot be copied. Transferring ownership is done by moving the pointer. 19 20`NonnullOwnPtr` is a special variant of `OwnPtr` with one additional property: it cannot be null. `NonnullOwnPtr` is suitable as a return type from functions that are guaranteed to never return null, and as an argument type where ownership is transferred, and the argument may not be null. In other words, if `OwnPtr` is "\*", then `NonnullOwnPtr` is "&". 21 22Note: A `NonnullOwnPtr` can be assigned to an `OwnPtr` but not vice versa. To transform an known-non-null `OwnPtr` into a `NonnullOwnPtr`, use `OwnPtr::release_nonnull()`. 23 24### Construction using helper functions 25 26There is a `make<T>()` helper that constructs a new object and returns it wrapped in a `NonnullOwnPtr`. All arguments passed to it are forwarded to `T`'s constructor. If it fails to allocate heap memory for the object, it terminates the program. 27 28```cpp 29{ 30 NonnullOwnPtr<Foo> my_object = make<Foo>(); 31 my_object->do_stuff(); 32 // my_object goes out of scope here, and the Foo will be deleted. 33} 34``` 35 36The `try_make<T>()` helper attempts to construct a new object wrapped in an `ErrorOr<NonnullOwnPtr<T>>`. All arguments passed to it are forwarded to `T`'s constructor. In case of allocation failure, an ENOMEM error is returned. This allows the calling code to handle allocation failure as it wishes. 37 38```cpp 39auto my_object_or_error = try_make<Foo>(); 40if (my_object_or_error.is_error()) { 41 // handle allocation failure... 42} 43auto my_object = my_object_or_error.release_value(); 44my_object->do_stuff(); 45``` 46 47Note: Objects constructed using `try_make<T>()` should only be dereferenced after a null check. 48 49### Manual construction 50 51The helper functions cannot access private constructors, so in some cases, smart pointers need to be created manually. This is done by "adopting" a raw pointer, which moves its ownership to the smart pointer. Dereferencing the raw pointer or calling its destructor afterwards can cause undefined behavior. 52 53Known non-null pointers can be turned into a `NonnullOwnPtr` by the global `adopt_own()` function. 54 55```cpp 56NonnullOwnPtr<Foo> my_object = adopt_own(*new Foo); 57``` 58 59It is safe to immediately dereference this raw pointer, as the normal `new` expression cannot return a null pointer. 60 61Any (possibly null) pointer to `T` can be turned into an `OwnPtr<T>` by the global `adopt_own_if_nonnull()` function. 62 63```cpp 64OwnPtr<Foo> my_object = adopt_own_if_nonnull(new (nothrow) Foo); 65``` 66 67In this case, the *non-throwing* `new` should be used to construct the raw pointer, which returns null if the allocation fails, instead of aborting the program. 68 69**Note:** Always prefer the helper functions to manual construction. 70 71---- 72## RefPtr\<T\> and NonnullRefPtr\<T\> 73 74`RefPtr` is used for multiple-owner objects. An object held by a `RefPtr` is owned together by every pointer pointing to that object. 75 76Shared ownership is implemented via reference counting. 77 78`NonnullRefPtr` is a special variant of `RefPtr` with one additional property: it cannot be null. `NonnullRefPtr` is suitable as a return type from functions that are guaranteed to never return null, and as an argument type where the argument may not be null. In other words, if `RefPtr` is "\*", then `NonnullRefPtr` is "&". 79 80Objects can only be held by `RefPtr` if they meet certain criteria. Specifically, they need to implement the functions `ref()` and `unref()`. 81 82To make a class `T` reference-counted, you can simply make it inherit from `RefCounted<T>`. This will add all the necessary pieces to `T`. 83 84```cpp 85class Bar : public RefCounted<Bar> { 86 ... 87}; 88``` 89 90Note: A `NonnullRefPtr` can be assigned to a `RefPtr` but not vice versa. To transform an known-non-null `RefPtr` into a `NonnullRefPtr`, either use `RefPtr::release_nonnull()` or simply dereference the `RefPtr` using its `operator*`. 91 92### Construction using helper functions 93 94There is a `make_ref_counted<T>()` global helper function that constructs a new object and returns it wrapped in a `NonnullRefPtr`. All arguments passed to it are forwarded to `T`'s constructor. If memory cannot be allocated for the object, the program is terminated. 95 96```cpp 97NonnullRefPtr<Bar> our_object = make_ref_counted<Bar>(); 98NonnullRefPtr<Bar> another_owner = our_object; 99``` 100 101 102The `try_make_ref_counted<T>()` function constructs an object wrapped in `ErrorOr<NonnullRefPtr<T>>` which may be an error if the allocation does not succeed. This allows the calling code to handle allocation failure as it wishes. All arguments passed to it are forwarded to `T`'s constructor. 103 104```cpp 105auto our_object_or_error = try_make_ref_counted<Bar>(); 106if (our_object_or_error.is_error()) { 107 // handle allocation failure... 108} 109NonnullRefPtr<Bar> our_object = our_object_or_error.release_value(); 110RefPtr<Bar> another_owner = our_object; 111``` 112 113In the above examples, the Bar object will only be deleted once both `our_object` and `another_owner` are gone. 114 115### Manual construction 116 117The helper functions cannot access private constructors, so in some cases, objects need to be manually wrapped into smart pointers. When constructing an object that derives from `RefCounted`, the reference count starts out at 1 (since 0 would mean that the object has no owners and should be deleted). The object must therefore be "adopted" by someone who takes responsibility of that 1. The raw pointer must not be used after its ownership is transferred to the smart pointer. 118 119A known non-null raw pointer can be turned into a `NonnullRefPtr` by the global `adopt_ref()` function. 120 121```cpp 122NonnullRefPtr<Bar> our_object = adopt_ref(*new Bar); 123``` 124 125Note: It is safe to immediately dereference this raw pointer, as the normal `new` expression cannot return a null pointer. 126 127Any (possibly null) pointer to a reference-counted object can can be turned into a `RefPtr` by the global `adopt_ref_if_nonnull()` function. 128 129```cpp 130RefPtr<Bar> our_object = adopt_ref_if_nonnull(new (nothrow) Bar); 131``` 132In this case, the *non-throwing* `new` should be used to construct the raw pointer, which returns null if the allocation fails, instead of aborting the program. 133 134**Note:** Always prefer the helper functions to manual construction. 135 136---- 137## WeakPtr\<T\> 138 139`WeakPtr` is used for objects that somebody else owns. When the pointee of a `WeakPtr` is deleted, the `WeakPtr` will magically become null. 140 141Behind the scenes, this is implemented using the `Weakable` template. If you want to make it possible for a class `T` to be weakly-pointed-to, have it inherit from `Weakable<T>`. 142 143To create a `WeakPtr` to a weakable object, use `make_weak_ptr()`: 144 145```cpp 146class Baz : public Weakable<Baz> { 147 .... 148}; 149 150WeakPtr<Baz> a_baz; 151{ 152 NonnullOwnPtr<Baz> my_baz = make<Baz>(); 153 a_baz = my_baz->make_weak_ptr(); 154 // a_baz now points to my_baz 155} 156// a_baz is now null, since my_baz went out of scope. 157```