Sometimes I want to delegate the construction of objects that a class owns to a separate function. Something like
Vertex* new_vertex(const Options& options) {
// do stuff...
return new Vertex(...);
}
where the function is only intended to be used to within a class that owns the Vertex
. Clearly this function can cause some memory-leaking confusion, so I want to make it as clear as possible. Is there a naming convention for such functions?
3 Answers 3
Return a unique_ptr
:
std::unique_ptr<Vertex> new_vertex(const Options& options) {
// do stuff...
return std::make_unique<Vertex>(...);
}
There can only ever be one unique_ptr
pointing to a given object (unless you abuse it by casting to a Vertex*
and back, anyway). You can't ever copy a unique_ptr
, only move it. When a unique_ptr
is destroyed (and hasn't been moved out of) it even delete
s the object for you.
make_unique
creates a new Vertex
and wraps it in a unique_ptr
; the arguments you pass to make_unique
are the arguments it passes to the constructor.
-
4This is helpful, but it doesn't really answer my question. I know about smart pointers, I just wanted to know if there's a naming convention for functions that return raw pointers that the user has to take care of. I guess the answer is "no, there isn't, because you should never do that"?Shep– Shep2015年07月15日 07:30:36 +00:00Commented Jul 15, 2015 at 7:30
-
3@Shep If you want to indicate to the person calling your function that it creates a new object,
unique_ptr
does that. If it has to be a name for some reason, then I guess it doesn't, but why does it have to be a name?Stack Exchange Broke The Law– Stack Exchange Broke The Law2015年07月15日 07:32:26 +00:00Commented Jul 15, 2015 at 7:32 -
8@Shep Names are intangible things and everyone is free to ignore them completely. Types on the other hand are enforced by the compiler, which means it requires significant effort to break stuff. Never send a name to do a type's job.ComicSansMS– ComicSansMS2015年07月15日 09:05:39 +00:00Commented Jul 15, 2015 at 9:05
-
1@Shep there's no harm returning a raw pointer in this case. This case being that you've created an object that it is up to the caller to manage the lifetime of. The caller can always drop it straight into a smart pointer if they think they need to.Grimm The Opiner– Grimm The Opiner2015年07月15日 14:43:49 +00:00Commented Jul 15, 2015 at 14:43
-
1@immibis: Why fo you override the template-argument-deduction of
std::make_unique
? That's unneccessarily verbose and error-prone...Deduplicator– Deduplicator2015年07月19日 01:09:38 +00:00Commented Jul 19, 2015 at 1:09
Is there a standard way to indicate that a function returns a new pointer?
No, there is no "standard way" (but there is an API design policy currently considered "best practice").
Because of this ambiguity ("what does a function returning a pointer want me to do with it?"), it is currently considered best practice to impose the lifetime and ownership policy, through the return type:
<vertex pointer type> new_vertex(const Options& options);
<vertex pointer type>
can be std::unique_ptr
("new_vertex doesn't own the pointer"), or std::shared_ptr
("client code doesn't own the pointer"), or something else, that has clearly defined ownership semantics (for example, Vertex const * const
would indicate to client code "read the address and values, but change neither/don't delete the pointer").
Generally, you should not return a raw pointer (but in some cases, "practicality beats purity").
TLDR: there is a best practice (yes), but not a standard way (in the language).
Edit:
where the function is only intended to be used to within a class that owns the Vertex
If the class owns the Vertex, I would write it like this:
class SomeClass // owns the vertex
{
void do_stuff() // uses the vertex internally
{
init_vertex(); // see below
assert(vertex.get());
// use vertex as needed
}
private:
// "class owns the Vertex"
std::unique_ptr<Vertex> vertex;
// sets the internal vertex
// function doesn't return a pointer of any kind
void init_vertex(const Options& options); // or "reset_", "refresh_",
// or "make_" vertex,
// if the pointer can change
// throughout the lifetime
// of a SomeClass instance
};
Yes, there are dozens of "standards" about this
The answer recommending unique_ptr
is probably the best answer, but if you're looking at raw pointers, everyone's developed their own standard.
Generally speaking functions named create_____
or new______
or alloc_____
tend to imply a new object.
However
Consider that you're mixing behaviors. You don't actually care whether this is a new instance of an object or not. What you care about is whether the caller is responsible for destroying the object or not. There are many APIs which hand over the responsibility for an existing object, and those functions will need a matching naming scheme. Perhaps give______
.
If you're willing to step a little away from C++...
If you're willing to consider Objective-C to be similar enough to C++ to be a source for an answer, there's actually a real answer in that language. Objective-C manages the lifespan of objects using reference counts. They needed a way to describe whether you were being given responsibility for an object, or merely a reference to an existing object. If you get the answer wrong, you get the ref-counts wrong and lose objects. Accordingly they made a rule: every object you are returned is owned by someone else (so you must increment and decrement the ref count yourself), except for creation calls which pass an already incremented pointer to you (you only have to decrement). These calls were identified by the method name. Methods starting with alloc
new
copy
or mutableCopy
always returned a new object. So there, the standard was enforced by the language.
// TODO: Fix allocation of raw pointer.
unique_ptr
by calling itsrelease()
function, and use the raw pointers like the old ways.// FIXME: Allocation of raw pointer
?new_vertex
so I know the object is newly minted. You could call itCreate_new_vertex
to be extra clear. As for the idea that you shouldn't manage heap memory without smart pointers, never seen the truth in that - in fact if you can't manage heap memory without them, you've got no business managing heap memory with them either!