如果对象调整自己的容器大小会怎样?

时间:2013-08-02 15:25:46

标签: c++ memory-management stl

这不是一个关于你为什么要编写这样的代码的问题,而是一个关于方法如何与它所依赖的对象相关的问题。

如果我有一个结构:

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

我这样使用它:

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

如果push_back(...)中的doSomething(...)导致向量扩展会怎样?这意味着vec[0]将被复制,然后在执行其方法时被删除。这不好。

有人能解释一下这里究竟发生了什么吗?

  • 程序是否会立即崩溃?该方法是否只是尝试对不存在的数据进行操作?
  • 该方法是否会对其对象进行“孤立”操作,直到遇到更改对象状态等问题为止?

我对方法调用与关联对象的关系感兴趣。

2 个答案:

答案 0 :(得分:7)

是的,这很糟糕。当你在doSomething()中时,你的对象可能被复制(或者在C ++ 11中被移动,如果区别与你的代码相关)。因此,在push_back()返回后,this指针可能不再指向对象的位置。对于vector :: push_back()的特定情况,可能已释放此指向的内存,并将数据复制到其他位置的新数组中。对于将其元素留在原位的其他容器(例如列表),这(可能)根本不会导致问题。

实际上,您的代码不太可能立即崩溃。最可能的情况是写入空闲内存和F对象状态的静默损坏。您可以使用valgrind之类的工具来检测这种行为。

但基本上你有正确的想法:不要这样做,这不安全。

答案 1 :(得分:3)

  

有人能解释一下这里究竟发生了什么吗?

是。 如果您访问该对象,在push_backresizeinsert重新分配了vector的内容后,这是未定义的行为,这意味着什么实际发生取决于您的编译器,您的操作系统,do some more stuff是什么以及可能还有许多其他因素,例如可能是月相,某些遥远位置的空气湿度,......你将它命名; - )

简而言之,这是(通过std::vector实现)间接调用对象本身的析构函数,因此对象的生命周期已经结束。此外,vector的分配器已释放先前由对象占用的内存。因此,使用对象的非静态成员会导致未定义的行为,因为传递给函数的this指针不再指向对象。但是,您可以访问/调用该类的静态成员:

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
  }
}