Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens if an object resizes its own container?

This is not a question about why you would write code like this, but more as a question about how a method is executed in relation to the object it is tied to.

If I have a struct like:

struct F
{
    // some member variables
    void doSomething(std::vector<F>& vec)
    {
        // do some stuff
        vec.push_back(F());
        // do some more stuff
    }
}

And I use it like this:

std::vector<F>(10) vec;
vec[0].doSomething(vec);

What happens if the push_back(...) in doSomething(...) causes the vector to expand? This means that vec[0] would be copied then deleted in the middle of executing its method. This would be no good.

Could someone explain what exactly happens here?

  • Does the program instantly crash? Does the method just try to operate on data that doesn't exist?
  • Does the method operate "orphaned" of its object until it runs into a problem like changing the object's state?

I'm interested in how a method call is related to the associated object.

like image 598
user487100 Avatar asked Aug 02 '13 15:08

user487100


2 Answers

Yes, it's bad. It's possible for your object to be copied (or moved in C++11 if the distinction is relevant to your code) while your are inside doSomething(). So after the push_back() returns, the this pointer may no longer point to the location of your object. For the specific case of vector::push_back(), it's possible that the memory pointed to by this has been freed and the data copied to a new array somewhere else. For other containers (list, for example) that leave their elements in place, this is (probably) not going to cause problems at all.

In practice, it's unlikely that your code is going to crash immediately. The most likely circumstance is a write to free memory and a silent corruption of the state of your F object. You can use tools like valgrind to detect this kind of behavior.

But basically you have the right idea: don't do this, it's not safe.

like image 101
Andy Ross Avatar answered Nov 07 '22 23:11

Andy Ross


Could someone explain what exactly happens here?

Yes. If you access the object, after a push_back, resize or insert has reallocated the vector's contents, it's undefined behavior, meaning what actually happens is up to your compiler, your OS, what do some more stuff is and maybe a number of other factors like maybe phase of the moon, air humidity in some distant location,... you name it ;-)

In short, this is (indirectly via the std::vector implemenation) calling the destructor of the object itself, so the lifetime of the object has ended. Further, the memory previously occupied by the object has been released by the vector's allocator. Therefore the use the object's nonstatic members results in undefined behavior, because the this pointer passed to the function does not point to an object any more. You can however access/call static members of the class:

struct F
{
  static int i;
  static int foo();

  double d;
  void bar();

  // some member variables
  void doSomething(std::vector<F>& vec)
  {
    vec.push_back(F());

    int n = foo(); //OK
    i += n;        //OK

    std::cout << d << '\n'; //UB - will most likely crash with access violation
    bar();                  //UB - what actually happens depends on the 
                            //     implementation of bar
  }
}
like image 26
Arne Mertz Avatar answered Nov 07 '22 23:11

Arne Mertz