简体   繁体   中英

c++11 member function returns vector of raw pointers from vector of unique_ptr

I am starting using c++11 features and I like to use smart pointers only to own the objects. Here is my class:

class MyClass {
  public:
    vector<MyObject*> get_objs() const;

  private:
    vector<unique_ptr<MyObject>> m_objs;
};

The semantics is that MyClass owns a serial of MyObject which are created through make_unique (). get_objs() returns a vector of raw pointers in order for various callers to update the objects. Because those callers do not own the objects, so the function does not return vector<unique_ptr>.

But this means I need to implement get_objs() like this:

vector<MyObjects*> MyClass::get_objs() const
{
  vector<MyObjects*> ret;
  for (auto obj : my_objs) {
    ret.push_back(obj->get());
  }
  return ret;
}

My concern is get_objs() is called fairly often, each time there is an overhead to construct this raw pointer vector.

Is there something I could do here? If there is no c++11 tricks to save the overhead, should I just use type vector<MyObject*> for m_objs in the first place?

UPDATE 1

Jonathan Wakely's solution using operator[] improves mine so that caller can access individual object directly.

Is there any other solution? I do not mind go over all the places calling get_objs(), but like to see if there is even better solution.

Another note - I cannot use BOOST, just some restriction I have to live with.

For a start you can use ret.reserve(m_objs.size()) to pre-allocate the right number of elements.

Alternatively, don't return a vector for callers to iterate over directly, but expose a vector-like interface instead:

class MyClass {
  public:
    struct iterator;
    iterator begin();
    iterator end();
    MyObject* operator[](size_t n) { return m_objs[n].get(); }

  private:
    vector<unique_ptr<MyObject>> m_objs;
};

This allows the callers to modify the objects directly, rather than getting a container of pointers.

class MyClass {
  public:
   std::vector<std::unique_ptr<MyObject>> const& get_objs() const {
     return m_objs;
   }

  private:
    std::vector<std::unique_ptr<MyObject>> m_objs;
};

a const std::unique_ptr<MyObject>& cannot steal ownership, and is not the same as a std::unique_ptr<const MyObject> . A const std::vector<std::unique_ptr<MyObject>>& can only grant const access to its data.

In I would instead do this:

class MyClass {
  public:
   std::span<std::unique_ptr<MyObject> const> get_objs() const {
     return {m_objs.begin(), m_objs.end()};
   }

  private:
    std::vector<std::unique_ptr<MyObject>> m_objs;
};

which hides the implementation detail of "I am storing it in a vector" while exposing "I am storing it contiguously".

Prior to , I advise finding or writing your own span type if you have the budget. They are quite useful.

If you can use Boost, try indirect_iterator ( http://www.boost.org/doc/libs/1_55_0b1/libs/iterator/doc/indirect_iterator.html ). You need to define iterator, begin and end in your class:

typedef boost::indirect_iterator<vector<unique_ptr<MyObject>::iterator> iterator;
iterator begin() { return make_indirect_iterator(m_objs.begin()); }

Then your class exposes iterator, the value of which is reference (not pointer!) to MyObject . You can iterate and access the elements of the vector directly.

For the record, I think something like Jonathan Wakely's answer is the way to go. But since you asked for more possibilities, another one is to use shared_ptr instead of unique_ptr :

class MyClass {
  public:
    const vector<shared_ptr<MyObject>>& get_objs() const {
        return m_objs;
    }

  private:
    vector<shared_ptr<MyObject>> m_objs;
};

This improves the original code in two ways:

  1. There is no longer any need to build up a new vector in get_objs ; you can just return a reference to the one you have.
  2. You no longer need to worry about wild pointers in the case where a caller keeps the return value alive longer than the object that returned it-- shared_ptr ensures the pointed-to objects aren't deleted until all references have been released.

On another note, get_objs arguably should not be const . Calling code can't modify the vector itself, but it can modify the MyObject s it contains.

Another way is not to return any objects at all and encapsulate how you are storing the data (tell don't ask). Usually when you get the items you end up iterating through them all, so it makes sense to wrap that up in the class and pass in a function you want your objects to pass through.

The non boost way will be something like

void MyClass::VisitItems(std::function<void, MyObject&> f)
{
    for (auto obj : my_objs)
    {
        f(*obj);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM