After changing my code to use unique pointers, I stumbled upon how to return a collection of objects to a client. In general, I want to pass objects as references or non-owning pointers. But if I have a collection of objects, I can't just return the reference to it.
As an example, I have a simple class with a collection of objects, which are all created once and not altered afterwards.
using ObjectUPtr = std::unique_ptr<Object>;
class MyClass
{
public:
const std::vector<Object*>& GetObjectsOldStyle() const
{
return mObjectsOldStyle;
}
const std::vector<VObjectUPtr>& GetObjectsNewStyleA() const
{
// I don't like that: The client should not see the unique_ptr ...
return mObjectsNewStyle;
}
std::vector<VObject*> GetObjectsNewStyleB() const
{
// Ok, but performance drops
std::transform(...); // Transform the collection and return a copy
}
const std::vector<VObject*>& GetObjectsNewStyleC() const
{
// Ok, only copied once, but two variables per collection needed
// Transform the collection and cache in a second vector<Object*>
std::transform(...);
}
std::vector<Object*> mObjectsOldStyle; // old-style owning pointers here
std::vector<ObjectUPtr> mObjectsNewStyle; // how I want to do it today
}
Today, I usually prefer GetObjectsNewStyleB, but I wonder, if there is a more elegant and efficient way or a general best practice on how to return such collections.
If a function returns a std::unique_ptr<> , that means the caller takes ownership of the returned object. class Base { ... }; class Derived : public Base { ... }; // Foo takes ownership of |base|, and the caller takes ownership of the returned // object.
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
unique_ptr. An unique_ptr has exclusive ownership of the object it points to and will destroy the object when the pointer goes out of scope.
I would recommend creating your own iterator class. Then create begin and end member functions. You can even overload the dereference operator to return references, instead of pointers (unless your pointers might be null). It might start something like this:
class iterator :
public std::iterator<std::random_access_iterator_tag, Object>
{
public:
Object& operator*() const { return **base; }
Object* operator->() const { return &**base; }
iterator& operator++() { ++base; return *this; }
// several other members necessary for random access iterators
private:
std::vector<ObjectUPtr>::iterator base;
};
It's a bit tedious implementing a standard conforming iterator, but I think this is by far the most idiomatic solution. As mentioned in the comments, the Boost.Iterator library, specifically boost::iterator_facade
can be used to relieve some of the tedium.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With