diff options
author | Daniel Bertalan <dani@danielbertalan.dev> | 2021-06-28 10:56:51 +0200 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2021-06-29 22:57:52 +0430 |
commit | 754ddda38abe580fb91d1a05a5c07e848d38845b (patch) | |
tree | e3283efe928f293a82e356ff868bb769e1b99ce8 | |
parent | a5b4b95a763cc0972e0e1761d3963424b86559b6 (diff) | |
download | serenity-754ddda38abe580fb91d1a05a5c07e848d38845b.zip |
Documentation: Document changes to creating smart pointers
Because of the added complexity of *non-throwing* `new`, helper methods
for correctly constructing smart pointers were added in a previous
commit. This commit changes the documentation to recommend using these,
and adds examples to aid in correctly determining when to use
non-throwing new when manually creating smart pointers.
-rw-r--r-- | Documentation/SmartPointers.md | 90 |
1 files changed, 82 insertions, 8 deletions
diff --git a/Documentation/SmartPointers.md b/Documentation/SmartPointers.md index a6d456e02b..6b332c72ed 100644 --- a/Documentation/SmartPointers.md +++ b/Documentation/SmartPointers.md @@ -15,9 +15,15 @@ The reason for using these pointers is to make it explicit through code who owns This means that the `OwnPtr` is responsible for deleting the pointee when the `OwnPtr` goes out of scope. +These pointers cannot be copied. Transferring ownership is done by moving the pointer. + `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 "&". -There is a `make<T>()` helper that creates a new object and returns it wrapped in an `NonnullOwnPtr`. +Note: 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()`. + +### Construction using helper functions + +There 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. ```cpp { @@ -27,7 +33,39 @@ There is a `make<T>()` helper that creates a new object and returns it wrapped i } ``` -Note: 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()`. +The `try_make<T>()` helper attempts to construct a new object wrapped in an `OwnPtr`. All arguments passed to it are forwarded to `T`'s constructor. In case of allocation failure, a null pointer is returned. This allows the calling code to handle allocation failure as it wishes. + +```cpp +OwnPtr<Foo> my_object = try_make<Foo>(); +if (!my_object) { + // handle allocation failure... +} +my_object->do_stuff(); +``` + +Note: Objects constructed using `try_make<T>()` should only be dereferenced after a null check. + +### Manual construction + +The 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. + +Known non-null pointers can be turned into a `NonnullOwnPtr` by the global `adopt_own()` function. + +```cpp +NonnullOwnPtr<Foo> my_object = adopt_own(*new Foo); +``` + +It is safe to immediately dereference this raw pointer, as the normal `new` expression cannot return a null pointer. + +Any (possibly null) pointer to `T` can be turned into an `OwnPtr<T>` by the global `adopt_own_if_nonnull()` function. + +```cpp +OwnPtr<Foo> my_object = adopt_own_if_nonnull(new (nothrow) Foo); +``` + +In 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. + +**Note:** Always prefer the helper functions to manual construction. ---- ## RefPtr<T> and NonnullRefPtr<T> @@ -42,20 +80,56 @@ Objects can only be held by `RefPtr` if they meet certain criteria. Specifically To make a class `T` reference-counted, you can simply make it inherit from `RefCounted<T>`. This will add all the necessary pieces to `T`. -**Note:** 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. This is done through the global `adopt_ref()` function: - ```cpp class Bar : public RefCounted<Bar> { ... }; +``` + +Note: 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*`. + +### Construction using helper functions + +There is a `create<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. + +```cpp +NonnullRefPtr<Bar> our_object = create<Bar>(); +NonnullRefPtr<Bar> another_owner = our_object; +``` + -RefPtr<Bar> our_object = adopt_ref(*new Bar); +The `try_create<T>()` function constructs an object wrapped in `RefPtr<T>` which may be null 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. + +```cpp +RefPtr<Bar> our_object = try_create<Bar>(); +if (!our_object) { + // handle allocation failure... +} RefPtr<Bar> another_owner = our_object; ``` -In the above example, the Bar object will only be deleted once both `our_object` and `another_owner` are gone. +In the above examples, the Bar object will only be deleted once both `our_object` and `another_owner` are gone. -Note: 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*`. +### Manual construction + +The 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. + +A known non-null raw pointer can be turned into a `NonnullRefPtr` by the global `adopt_ref()` function. + +```cpp +NonnullRefPtr<Bar> our_object = adopt_ref(*new Bar); +``` + +Note: It is safe to immediately dereference this raw pointer, as the normal `new` expression cannot return a null pointer. + +Any (possibly null) pointer to a reference-counted object can can be turned into a `RefPtr` by the global `adopt_ref_if_nonnull()` function. + +```cpp +RefPtr<Bar> our_object = adopt_ref_if_nonnull(new (nothrow) Bar); +``` +In 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. + +**Note:** Always prefer the helper functions to manual construction. ---- ## WeakPtr<T> @@ -73,7 +147,7 @@ class Baz : public Weakable<Baz> { WeakPtr<Baz> a_baz; { - OwnPtr<Baz> my_baz = make<Baz>(); + NonnullOwnPtr<Baz> my_baz = make<Baz>(); a_baz = my_baz->make_weak_ptr(); // a_baz now points to my_baz } |