Home > other >  How to define a unique object in a for loop in C
How to define a unique object in a for loop in C

Time:08-09

I have java/python experience and recently started working on a C project. I ran into this very generic problem and wasn't able to find any clear answer/explanation in StackOverflow. The concepts behind is probably very basic, but still most of the things I tried seems to fail this far, so I thought it is worth to post it here and learn the "proper C way".

I'm trying to create a vector of objects in a for loop. The code I have is something like below which works:

  int NUMBER_OF_OBJECTS = 6;
  std::vector<SomeObject> objects = std::vector<SomeObject>(NUMBER_OF_OBJECTS);
  for (int i = 0; i < NUMBER_OF_OBJECTS; i  ) {
    std::string name = "Object"   std::to_string(i);
    test::SetObject(&objects[i], name.data());
  }

however if I try something like:

  std::vector<SomeObject> objects;
  for (int i = 0; i < 6; i  ) {
    std::unique_ptr<SomeObject> object = std::make_unique<SomeObject>();
    objects.push_back(*object.get());
    std::string name = "Object"   std::to_string(i);
    test::SetObject(&objects[i]);
  }

it doesn't work (ie below prints the same address):

for (SomeObject o : objects) {
  std::cout << "object address: " << &o << "\n";
}

I thought that's because the docs of unique_ptr says "The object is destroyed and its memory deallocated when ... unique_ptr managing the object is destroyed". So I tried to preserve the unique_ptr by creating a vector of it, something like:

  std::vector<std::unique_ptr<SomeObject>> objects;
  for (int i = 0; i < 6; i  ) {
    std::unique_ptr<SomeObject> object = std::make_unique<SomeObject>();
    objects.push_back(object);
    std::string name = "Object"   std::to_string(i);
    test::SetObject(&(*objects[i].get()));
  }

but instead of preserving the unique_ptr, this instead still destroys the object and warns me call to implicitly-deleted copy constructor of 'std::unique_ptr<SomeObject>'

I guess putting the unique_ptr to a vector is not enough to make C not destroy that unique_ptr, maybe I need to do something like std::move? I feel like something as basic as initialising a list of objects is probably done very frequently and should have a simpler way to it.

I would greatly appreciate if anyone could elaborate how memory management works in C for practical purposes (eg. I know I could use new, but read that in most cases I should use make_unique instead to avoid leaking memory)

CodePudding user response:

It's not your other code that is flawed, it's the loop where you print the addresses to the objects:

for (SomeObject o : objects) {
  std::cout << "object address: " << &o << "\n";
}

Here a brand new o object will be created each iteration of the loop. But nothing stops the compiler to reuse the same memory for the object o.

If you want to get each "unique" object from the container, use references instead:

for (SomeObject const& o : objects) { ... }

CodePudding user response:

std::unique_ptr<SomeObject> object = std::make_unique<SomeObject>();
objects.push_back(*object.get());

can be simplified to

SomeObject object{};
objects.push_back(object);

or even

objects.emplace_back();

Your issue in 2nd/3rd snippet is that vector is resized, so inernal pointer and iterator are invalidated:

test::SetObject(&objects[i]); // dangling pointer after objects.push_back(*object.get());

std::vector::reserve might solve your issue:

std::vector<SomeObject> objects;
object.reserve(6);
for (int i = 0; i < 6; i  ) {
    SomeObject object{};
    objects.push_back(object);
    std::string name = "Object"   std::to_string(i);
    test::SetObject(&objects[i]);
}

or use vector of (smart) pointer:

std::vector<std::unique_ptr<SomeObject>> objects;
// object.reserve(6); // No longer needed
for (int i = 0; i < 6; i  ) {
    std::unique_ptr<SomeObject> object = std::make_unique<SomeObject>();
    objects.push_back(std::move(object));
    // or simply
    // objects.push_back(std::make_unique<SomeObject>());
    std::string name = "Object"   std::to_string(i);
    test::SetObject(objects[i].get());
}
  •  Tags:  
  • c
  • Related