Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ - Adding objects to an std::vector, instantiated in a loop

I'm an expert level Java programmer, trying to port my knowledge over to C++. This is not homework, just a concept that I'm trying to learn the C++ equivalent of.

What I'm trying to do, is "generate" a list of objects of a custom type using a loop. This is how I would do it in Java:

public class TestClass
{
   private ArrayList<ExampleClass> _exampleObjects;
   private int _numObjects = 10;

   public void populateList()
   {
      _exampleObjects = new ArrayList<ExampleClass>();

      for(int i = 0; i < _numObjects; i++)
      {
         _exampleObjects.add(new ExampleClass());
      }
   }

   public void doStuffWithListItems()
   {
      for(ExampleClass e : _exampleObjects)
      {
         e.performAction();
      }
   }
}

Super simple stuff. Create a list, iterate through an arbitrary loop and add objects to it. Then, loop through those objects and use them for whatever purpose.

TestClass.h:

class TestClass
{
   public:
      // Constructor, copy constructor, destructor definitions

      void populateList();
      void doStuffWithListItems(); 
   private:
      std::vector<ExampleClass> _exampleObjects;
      const int _numObjects = 10;
};

TestClass.cpp:

void TestClass::populateList()
{
   for(int i = 0; i < _numObjects; i++)
   {
      ExampleObject obj;
      _exampleObjects.push_back(obj); 

      /* What actually goes here in place of obj? */
   }
}

void TestClass::doStuffWithListItems()
{
   for(auto it = _exampleObjects.begin(); it != _exampleObjects.end(); it++)
   {
      /* What do i do with my iterator to access my object? */
   }
}

Its my understanding that where I initialise my objects in the first loop, they go out of scope and die by the end of each loop iteration. Is that right? If so, how do I make a persistent instance?

I experimented with the shared_ptr<> from and was apparently able to store them persistently, but couldn't for the life of me work out how to dereference from an iterator of a shared_ptr<>.

I feel like this should be a really simple concept. I just can't seem to work it out. I've read a lot on C++ scope and loops. I just can't seem to find anything on both.

like image 498
Scott Drew Avatar asked Nov 04 '13 23:11

Scott Drew


2 Answers

ExampleObject obj;
_exampleObjects.push_back(obj); 

/* What actually goes here in place of obj? */

Nothing. What you have is correct, assuming ExampleClass has a working copy constructor. If your compiler supports C++11 (and since you're using auto, it at least partially does), you can save yourself a copy.

_exampleObjects.emplace_back();

This constructs an object in place in the vector, forwarding the arguments (none in this case) to a matching constructor (the default ctor, in this case). For accessing the object from the iterator, do this:

for(auto it = _exampleObjects.begin(); it != _exampleObjects.end(); it++)
{
   it->performAction();
}

Again, C++11 can make things better here.

for(auto & obj : _exampleObjects)
{
    obj.performAction();
}

Its my understanding that where I initialise my objects in the first loop, they go out of scope and die by the end of each loop iteration.

Correct.

If so, how do I make a persistent instance?

vector<>::push_back takes care of this. It copies the parameter into the vector. In other words, it's not the same object that was created in the loop, it's a copy. You just need to ensure that ExampleClass has non-broken copy semantics.

couldn't for the life of me work out how to dereference from an iterator of a shared_ptr<>

If you had an iterator into a vector of shared pointers, (call it it), you would dereference it, and call the member function of the stored object, like this:

(*it)->performAction();
// alternatively
(**it).performAction();
like image 54
Benjamin Lindley Avatar answered Oct 15 '22 04:10

Benjamin Lindley


The ideal answer suggests a very bad idea - use post increment ++ on iterator in loop. You should never ever use it in loops where you only need to iterate because postincrement must return the value the iterator had before it was incrementing; so, that previous value needs to be copied somewhere before. It is just not good from performance perspective and a bad codestyle sign.

like image 42
Alexey Zhivotov Avatar answered Oct 15 '22 06:10

Alexey Zhivotov