I want to know - are there any guidelines about syntax of C++ (non-)member functions, that allows me to understand (without comments, if possible) the ownership policy of its arguments and return value. By ownership I mean, that the owner is responsible for the destruction of the owned object.
I distinguish the following rules about arguments:
- take ownership
- don't take ownership
- share
and about return value:
- release ('return by value' is in this group)
- don't release
- share
For example, passing object by reference doesn't take it's ownership:
void func(object & obj) { ... }
Such guidelines may use standard constructions like unique_ptr, shared_ptr, etc. If there are no such guidelines, then the examples of possible syntax misunderstandings are welcome too.
I can't see why using smart pointers doesn't suffice. I can't think of anything else that I wouldn't categorize as code smell. Using smart pointers over raw pointers makes ownership and responsebilities perfectly clear:
auto_ptr
/unique_ptr
- single owner, ownership is transferredshared_ptr
- multiple owners, ownership may be transferredscoped_ptr
- single owner, ownership cannot be transferredweak_ptr
- observer (butshared_ptr
may be created fromweak_ptr
)
I think that these suffice to clearly show the responsibilities, e.g.
void func(std::auto_ptr<Class> input) {...} // func() takes ownership of input
void func(std::shared_ptr<Class> input) {...} // func() and caller share ownership
std::auto_ptr<Class> func() {...} // caller takes ownership of returned value
std::shared_ptr<Class> func() {...} // func() and caller shares ownership of returned object
std::weak_ptr<Class> func() {...} // func() owns created object, but caller may observe it
As you mention, references are also great in this sense. Note if there's a need to free the pointers using some custom mechanism, shared_ptr
and unique_ptr
supports custom deleters. auto_ptr
does not have this capability.
Note! If you are using C++ pre-11, you'll have to resort to boost::shared_ptr
and boost:weak_ptr
.
If I understand you, then Boost call_traits might be what you are looking for:
The example (copied from the docs goes):
template <class T>
struct contained
{
// define our typedefs first, arrays are stored by value
// so value_type is not the same as result_type:
typedef typename boost::call_traits<T>::param_type param_type;
typedef typename boost::call_traits<T>::reference reference;
typedef typename boost::call_traits<T>::const_reference const_reference;
typedef T value_type;
typedef typename boost::call_traits<T>::value_type result_type;
// stored value:
value_type v_;
// constructors:
contained() {}
contained(param_type p) : v_(p){}
// return byval:
result_type value() { return v_; }
// return by_ref:
reference get() { return v_; }
const_reference const_get()const { return v_; }
// pass value:
void call(param_type p){}
};
Not much clearer than param_type
, reference_type
and return_type
to indicate what is meant.
I just use this syntax for parameters where needed:
example constructor declaration:
t_array(const t_ownership_policy::t_take& policy, THESpecialType* const arg);
In use at the callsite:
t_array array(t_ownership_policy::Take, THESpecialTypeCreate(...));
Where t_ownership_policy::t_take
is just a dummy overload disambiguator typename.
In this system, there are multiple policies, each with separate types. I favored unique types per policy because a typed enumeration (for example) does not support initialization as easily, and it's too easy to pass an unsupported policy to a function or constructor. 'Polymorphic' policies can reduce symbol count, but it's a pain because it pushes error detection to runtime.
For 'returning':
void func(t_container<t_type>& outValue);
Where t_container
is whichever pointer container type you choose. Then the container type already implements the necessary boilerplate. This container may be something like a shared_ptr
, or some specialization you have written.
and for more elaborate types, i'll often use syntax like so:
void func(t_special_container& outValue) {
...
outValue.take(ptr);
- or -
outValue.copy(ptr);
来源:https://stackoverflow.com/questions/7992600/syntax-guidelines-for-taking-ownership-and-releasing-objects-in-c