【问题标题】:What happens if an object resizes its own container?如果一个对象调整自己容器的大小会发生什么?
【发布时间】:2013-08-02 15:25:45
【问题描述】:

这不是关于为什么要编写这样的代码的问题,而是关于方法如何相对于它所绑定的对象执行的问题。

如果我有这样的结构:

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] 将在执行其方法的过程中被复制然后删除。这可不好。

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

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

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

【问题讨论】:

    标签: c++ memory-management stl


    【解决方案1】:

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

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

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

    【讨论】:

    • 感谢您的回答。您是说该程序可能会继续运行而不会崩溃吗?如果我将数组的边界写入空闲内存,则会检测到它。什么能让方法能够毫无问题地写入空闲内存?
    • 您几乎总是可以写入“已释放”的内存而不会崩溃。这就是内存映射的工作方式。地图保留在原地供未来的对象使用。但是,您可以永远“毫无问题地”写入已释放的内存。只是“问题”不是崩溃。
    • @user487100:如果您将数组的边界写入空闲内存,则有时会被检测到。制作一个案例非常容易,我可以写到最后并且暂时不会检测到它。分配一个char* a = new char[1],然后读写a[1],在大多数系统上应该不会触发任何错误。
    【解决方案2】:

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

    是的。 如果您访问该对象,在 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
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-20
      • 2022-01-23
      • 2017-09-22
      • 2014-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多