It’s well stablished that having raw pointers as part of a C++ API is not a good practice since it’s not clear who (The user or the API internals) manages the resource. Consider:
Let’s say you have an API that operates on int
buffers. The first function, factory()
, returns a new buffer ready to play with. The latter have_fun()
does something with a buffer.
When reading such declarations, I have no idea if when requesting a new buffer via a factory()
call if I’m in charge of deallocating it, or the return value from factory()
is just a handle to an internal resource managed by the library.
So, what’s the solution? Documentation? That’s what C has been doing for years: “RTFM, you shouldn’t call memcpy()
with negative numbers, so never use int
for sizes please.” Then we have heartbleed and similar bugs. Docs don’t prevent your code to compile. [begin rant] That’s why I prefer a language with a static type system, instead of void*
and cast-land [end rant].
Instead, using C++ resource handlers, owning semantics are clear:
“Oh, the library owns the buffer but it’s being shared with me.” That makes sense. But what about have_fun()
? Passing it as a std::shared_ptr<int>
has no much sense. Adding potential mutex lock for something that will not change the handle in any way. std::shared_ptr<int>&
? Too much syntactic nosise to just say “Hey, you don’t own the ptr ok? It’s RAII-managed by others”. So I will continue with the naked int*
.
C++ classes = RAII + properties
In general, I’m agree with the rule “RAII-ed resources for the API, naked ones for internals” not just because the (neigible?) performance gains but to reduce noise. But there’s something I like of owning resources trough objects: Properties. We usually wrap resources into objects to do RAII avoiding leaks thanks to our friend }
. But objects (classes) also provide member functions, which can be used to ask for the state and other properties of the resource with a simple and concise syntax: I’m the only one that feels array.size()
is far more readable than sizeof(array)/sizeof/T)
?.
Is there any intermediate solution? Raw resources with properties?
Raw resources, C++ version
Consider std::vector
. It manages a dynamic array. vector
has three kind of member functions: Special member functions (Constructors, destructor, etc to manage array lifetime), mutable functions (push_back()
, reserve()
, etc), and finally member functions that do not mutate the array itself but may change its data: at()
, front()
, back()`, etc.
What I propose is to split the implementation of resource handlers like vector
into two different classes: The one which contains the resource and non-mutating methods, and the one which does RAII and guarantees proper management of the resource (So mutating members belong here):
Note that raw_vector
does not own the dynamic array. It can be seen as a C++ view of a raw C dynamic array, with its same memory and performance footprint plus some C++ syntactic sugar. You can actually use this class directly as vector view (See bellow).
raw_vector
provides the non-mutable facilities, and on top of that vector
owns the resource through RAII and implements all the operations that need to mutate the array:
Note that vector
has two deleted constructors, those that accept a raw vector as parameter. Constructing a vector from a raw one should be discouraged since we don’t know who owns that buffer.
Now we are able to avoid RAII overhead in contexts where we know we are not owning nor changing a resource, like API internals, while having a RAII interface: