2013-08-02 36 views
8

这不是一个关于为什么要编写这样的代码的问题,而更多的是关于如何执行一个方法与它绑定的对象有关的问题。如果对象调整其自己的容器,会发生什么情况?

如果我有一个像结构:

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); 

发生什么情况,如果在doSomething(...)push_back(...)导致矢量扩展?这意味着vec[0]将被复制,然后在执行其方法的过程中被删除。这不会有好处。

有人可以解释究竟发生了什么吗?

  • 程序是否会立即崩溃?该方法是否试图对不存在的数据进行操作?
  • 该方法是否在对象的“孤立”中运行,直至遇到像更改对象状态一样的问题?

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

回答

7

是的,这很糟糕。如果您的对象在doSomething()内部,则可以将对象复制(或者在C++ 11中移动,如果区分与您的代码相关)。所以在push_back()返回之后,这个指针可能不再指向你的对象的位置。对于vector :: push_back()的特定情况,可能由此指向的内存已被释放,并将数据复制到其他位置的新数组中。对于其他容器(例如列表),将它们的元素留在原地,这可能(可能)不会导致任何问题。

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

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

+0

感谢您的答案。您是否认为该计划可能会继续进行而不会崩溃?如果我将数组的边界写入空闲内存,则会被检测到。什么可以让一种方法能够毫无问题地写入空闲内存? – user487100

+2

您几乎可以永久写入“释放”的内存而不会崩溃。这就是内存映射的工作方式。地图保持原位以供将来的物体使用。但是,您可以**从不**写入已释放的内存“没有问题”。只是“问题”不是崩溃。 –

+0

@ user487100:如果您将数组的边界写入空闲内存,则会检测到_sometimes_。制作一个案例我可以很容易地写下来,但是我可以写出超过最后的结果,而不是一段时间内检测出来。分配一个'char * a = new char [1]',然后读写'a [1]',它可能不会在大多数系统上触发任何错误。 –

3

有人可以解释究竟发生了什么吗?

是的。 如果访问该对象,经过push_backresizeinsert已经重新分配了vector的内容,这是不确定的行为,这意味着什么实际上发生的事情是到你的编译器,您的操作系统,有什么do some more stuff是,也许一些其他因素可能是月相,空气湿度在一些遥远的位置,......你的名字;-)

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

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 
    } 
} 
相关问题