vector pushback在c++11中移动了一个局部var,测试和混淆

vector push_back a moved local var in c++11 , test and confuse

本文关键字:var 局部 一个 测试 c++11 pushback 移动 vector      更新时间:2023-10-16

以下测试在g++4.8.1 中编译

int main()
{
    vector<string> v ;
    v.push_back("33333") ;
    v.push_back("44444") ;
    v.push_back("55555") ;
    {
        string x("xxxxx") ;
        v.push_back(x) ; //copy 
        cout << "x=" << x << endl ; //x=xxxxx
    } //checkpoint1
    {
        string y("yyyyy") ;
        v.push_back(std::move(y)) ; //move 
        cout << "y=" << y << endl ; //y=
    } //checkpoint2
    for(auto const i : v)
        cout << i << " " ; 
    cout << endl ; //33333 44444 55555 xxxxx yyyyy 
}

这是一个非常简单的来源,我的测试重点是std::move。如您所见,string x是局部var,而执行v.push_back(x)时,x被复制到向量v,因此xpush_back之后仍然有"xxxxx"。在检查点1之后,x消失了(它只是本地的),但向量v有它的值,因为x是在执行v.push_back(x)时复制的,所以它是可以的!!

至于string y,它被移动到向量v,因为使用了std::move,所以您可以参见cout << "y=" << y显示的是"y=",而不是"y=yyyyy",这是正确的行为。

我没有得到的是,在checkpoint2之后,字符串y,作为一个本地var,它的生命结束了,因此向量v作为y的所有者(因为ypush_back(std::move(y))移动到向量v)应该包含一个无效元素,因为y作为堆栈本地变量,在检查点2是生命终结!

我很困惑,在检查点2之后,向量v仍然有"yyyyy"y被移到向量v,如果向量v只有一个指针vptr = &y,由于y是堆栈内存中的局部var,在它的作用域用完后,堆栈就不见了,所以向量vptr是无用的,看起来这不是真的!

所以它必须是向量有自己的内存来保持"yyyyy"在自己的内存中,但如果这是既然是push_back(x),为什么还要麻烦std::move(y)呢?

我错过什么了吗?

您可以将std::string看作一个智能指针。std::string变量指向存储在其他地方的实际字符串数据。当您std::movestd::string时,会为新字符串提供一个指向该数据的指针,并清除旧字符串。

以下是一个非常简单的工作方式:

class MyString {
  public:
    // Move constructor
    MyString(MyString&& that)
      // Just make our string data point to the other string
      : string_data(that.string_data)
    {
      // And make sure the string we are moving from no longer points
      // to that data so it won't get freed when the other string
      // is destructed.         
      that.string_data = 0;
    }
    // Copy constructor
    MyString(const MyString& that)
      // We can't take the other string's data, so we need a copy
      : string_data(new char[strlen(that.string_data)+1])
    {
      strcpy(string_data,that.string_data);
    }
    // Assignment using copy and swap idiom.
    MyString& operator=(MyString that)
    {
      std::swap(string_data,that.string_data);
      return *this;
    }
    ~MyString()
    {
      // string_data may be null if it has been moved from, but
      // that's ok -- it is safe to delete a null pointer.
      delete [] string_data;
    }
  private:
    char *string_data;
};

y移动到矢量中时。字符串数据现在是矢量拥有的字符串的一部分,y不再与它有任何关系。当y超出范围时,它对矢量中的数据没有影响,因为y不再有指向它的指针。

移动构造函数与复制构造函数一样,涉及两个对象:(A)原始源对象;以及(b)新创建的目的地对象。

在您的情况下,局部变量是源对象,目标对象是push_back新创建的对象,该对象放置在矢量存储中。

移动构造函数和复制构造函数之间的区别在于,按照惯例,源对象可能会被修改,并且不希望再次使用(源对象是一个"过期"对象)。这允许类作者进行可能损坏源对象的优化,例如将资源从源对象转移("移动")到目标对象。

std::string的情况下,可能每个字符串都包含一个指向动态分配的数组的成员指针。move构造函数可以简单地将指针从源对象交换到目标对象。