2

I've encountered this piece of existing code, and being still new to C++, I don't understand why storing a local variable into a vector makes it still accessible later.

Here's the simplified flow of the code, where BackendCall is some class defined earlier:

void SaveBackendCall(BackendCall* backend_call,
                     vector<BackendCall>* logged_calls) {
  logged_calls->push_back(*backend_call);
}

void AddNewCall(vector<BackendCall>* logged_calls) {
  BackendCall backend_call;  // The local variable in question.
  SaveBackendCall(&backend_call, logged_calls);
}

vector<BackendCall> logged_calls;

AddNewCall(&logged_calls);

for (auto i = logged_calls->begin(); i != logged_calls->end(); ++i) {
  i->access_stuff();  // This still works?
}

Wouldn't the local variable backend_call in AddNewCall() be destroyed after the function returns? Then I don't understand what is actually stored in logged_calls.

Would it make sense to convert the vector of BackendCall objects into a vector of unique_ptrs?

Addison
  • 1,065
  • 12
  • 17

2 Answers2

4

As the commenters have noted, vector<T>::push_back(someT) pushes a copy of someT onto the vector. It's true that by the time you get to your iteration loop, the original variable named backend_call has already departed for the great stack in the sky (which shall never overflow); or possibly it's been reincarnated as i (who knows? I don't want to debate religion here). But its memory lives on in the form of a facsimile inside logged_calls's buffer.

dlf
  • 9,045
  • 4
  • 32
  • 58
2

The vector is a vector of BackendCall objects. When adding objects to a vector, the copy constructor is called.

If you look at SaveBackendCall(), you will notice that it is dereferencing the pointer, which means it is passing in a reference to the object, not the pointer. (If the vector stored pointers, this would be dangerous code since what it is pointing to is going away when the stack goes out of scope.)

So what is happening here is:

  1. AddNewCall() creates a local variable.
  2. It's passing a pointer to that variable to SaveBackendCall().
  3. SaveBackendCall() is passing a reference to the object to push_back().
  4. push_back() implicitly makes a copy of the BackendCall and adds it to the vector.

Depending on the complexity and size of the BackendCall object, that copy may be an expensive operation. This can become especially problematic when the push_back requires the vector to be resized.

Yes, IMO it would probably be beneficial to use a vector of smart pointers to avoid unnecessary copies.

Jason
  • 121
  • 3