Daniel Standage Daniel Standage - 3 months ago 12
C++ Question

Why are vector contents changed after the method scope finishes?

The following dummy program mimics the behavior of another program I'm troubleshooting.

#include <iostream>
#include <vector>

class A
{
public:
std::vector<int> data;

void DoTheThing()
{
while (data.size() < 10) {
data.push_back(1);
}
}
};

class B
{
public:
std::vector<A> objs;

B()
{
A one, two, three;
objs.push_back(one);
objs.push_back(two);
objs.push_back(three);
}

void DoTheThing()
{
for (auto obj: objs) {
obj.DoTheThing();
std::cout << "DEBUG length during=" << obj.data.size() << std::endl;
}
}
};

int main()
{
B b;
b.DoTheThing();
for (auto obj : b.objs) {
std::cout << "DEBUG length after=" << obj.data.size() << std::endl;
}
}


I compile and run as:

$ g++ -Wall --std=c++11 -o test test.cpp
$ ./test
DEBUG length during=10
DEBUG length during=10
DEBUG length during=10
DEBUG length after=0
DEBUG length after=0
DEBUG length after=0
$


For some reason the state of the
A
objects in
b
's
objs
vector is changing between the
b.DoTheThing()
call and the subsequent print statements. My question is what is happening? Are the
A
object
data
vectors going out of scope somehow and being deleted, or perhaps the entire
A
objects? It seems like a scoping issue--perhaps even a trivially simple one--but it's been long enough since I programmed in C++ that I'm not sure. How can I make the contents of the
data
vectors persist after the call to
b.DoTheThing()
in the other methods?

Answer

"For some reason the state of the A objects in b's objs vector is changing between the b.DoTheThing() call and the subsequent print statements. My question is what is happening?"

void DoTheThing()
{
  for(auto obj : objs)
   // ^^^^^^^ receive by value due to implicit copy

You are actually making a separate copy of objs into individual temporary objs. Those temporaries are destroyed after the B::DoThething() finishes. To avoid the copies, use reference:

  for(auto& obj : objs)
   // ^^^^^^^ receive by reference

The same is true for the similar loop in the main() as well.


Real question can be: "How to avoid such accidents?"

If you can afford, then make the copy constructor as explicit to avoid implicit copying:

class A {
public:
  A () = default;      
  explicit A(const A&) = default;  // implicit copy is avoided
  A (A&&) = default;  // to avoid RVO related issues
  ...
};

Here is a demo, which shows how the explicit generates compilation error to catch the problem in the for loop which accidentally makes copies.


The explicit brings its own syntax limitations. Some of them can be solved & some cannot. Refer following question for 1 such issue (& how it's solved):

What is the effect of 'explicit' keyword on the Return Value Optimization (RVO)?

Comments