Hello! We have access to std::unique_ptr, as many of you know, but we're not using it. I'd like to start using it. Before we do, I'd like to discuss how we use it, when we use it, and things to be aware of.
The style guide has the following to say (long quote, sorry): " Prefer to have single, fixed owners for dynamically allocated objects. Prefer to transfer ownership with smart pointers. "Ownership" is a bookkeeping technique for managing dynamically allocated memory (and other resources). The owner of a dynamically allocated object is an object or function that is responsible for ensuring that it is deleted when no longer needed. Ownership can sometimes be shared, in which case the last owner is typically responsible for deleting it. Even when ownership is not shared, it can be transferred from one piece of code to another. "Smart" pointers are classes that act like pointers, e.g. by overloading the * and -> operators. Some smart pointer types can be used to automate ownership bookkeeping, to ensure these responsibilities are met. std::unique_ptr is a smart pointer type introduced in C++11, which expresses exclusive ownership of a dynamically allocated object; the object is deleted when the std::unique_ptr goes out of scope. It cannot be copied, but can be moved to represent ownership transfer. std::shared_ptr is a smart pointer type that expresses shared ownership of a dynamically allocated object. std::shared_ptrs can be copied; ownership of the object is shared among all copies, and the object is deleted when the last std::shared_ptr is destroyed. It's virtually impossible to manage dynamically allocated memory without some sort of ownership logic. Transferring ownership of an object can be cheaper than copying it (if copying it is even possible). Transferring ownership can be simpler than 'borrowing' a pointer or reference, because it reduces the need to coordinate the lifetime of the object between the two users. Smart pointers can improve readability by making ownership logic explicit, self-documenting, and unambiguous. Smart pointers can eliminate manual ownership bookkeeping, simplifying the code and ruling out large classes of errors. For const objects, shared ownership can be a simple and efficient alternative to deep copying. Ownership must be represented and transferred via pointers (whether smart or plain). Pointer semantics are more complicated than value semantics, especially in APIs: you have to worry not just about ownership, but also aliasing, lifetime, and mutability, among other issues. The performance costs of value semantics are often overestimated, so the performance benefits of ownership transfer might not justify the readability and complexity costs. APIs that transfer ownership force their clients into a single memory management model. Code using smart pointers is less explicit about where the resource releases take place. std::unique_ptr expresses ownership transfer using C++11's move semantics, which are relatively new and may confuse some programmers. Shared ownership can be a tempting alternative to careful ownership design, obfuscating the design of a system. Shared ownership requires explicit bookkeeping at run-time, which can be costly. In some cases (e.g. cyclic references), objects with shared ownership may never be deleted. Smart pointers are not perfect substitutes for plain pointers. If dynamic allocation is necessary, prefer to keep ownership with the code that allocated it. If other code needs access to the object, consider passing it a copy, or passing a pointer or reference without transferring ownership. Prefer to use std::unique_ptr to make ownership transfer explicit. For example: std::unique_ptr<Foo> FooFactory(); void FooConsumer(std::unique_ptr<Foo> ptr); Do not design your code to use shared ownership without a very good reason. One such reason is to avoid expensive copy operations, but you should only do this if the performance benefits are significant, and the underlying object is immutable (i.e. std::shared_ptr<const Foo>). If you do use shared ownership, prefer to use std::shared_ptr. Do not use scoped_ptr in new code unless you need to be compatible with older versions of C++. Never use std::auto_ptr. Instead, use std::unique_ptr. " Now I'm a big fan of explicit ownership and moving ownership rather than sharing non-smart pointers, but I recognise that in our code-base, ownership is difficult to reason about in many cases. However, we do have quite a few cases where we manage lifetime scope with explicit delete calls and I'd like to start by eliminating those. Ie, using std::unique_ptr to manage lifetime not necessarily ownership. This is difficult though as we may pass these pointers to other methods or even other libprocess processes. In cases that we can reason about the lifetime of the various pointers, that should be fine, but we have to be careful. One option is to start by replacing these pointers with process::Owned. The downside to this approach is that it introduces more non-standard types and, because Owned is implemented using std::shared_ptr, doesn't move us closer to defining clear ownership. So the floor is open. Do we: 1) embrace std::unique_ptr, eliminate raw pointers except in rare, well-defined cases from a lifetime point of view? 2) eliminate raw pointers in favour of std::shared_ptr and std::unique_ptr then work to eliminate the std::shared_ptrs 3) use process::Owned everywhere and then find places where we can transition them to std::unique_ptr 4) do nothing; there's no great benefit to std::unique_ptr and this smart pointer thing is just a fad. - dominic -- Dominic Hamon | @mrdo | Twitter *There are no bad ideas; only good ideas that go horribly wrong.*
