C++ 移动构造函数和移动重载赋值运算符存在问题?

C++ 移动构造函数和移动重载赋值运算符存在问题?,c++,move,destructor,move-semantics,rvalue-reference,C++,Move,Destructor,Move Semantics,Rvalue Reference,fredoverflow(用户237K代表)在他的两个答案中解释了大部分内容 但是,在实现Move构造函数和重载Move赋值操作符(OMAO)的过程中(我在整个问题中使用了这些简短的形式),我面临一些问题,我将在这里提出 另外还有一个由用户Greg Hewgill(代表826K的用户)给出的答案 我引用他的话 假设你 有一个函数返回一个重要的对象然后是普通的C++ 编译器将为multiply()的结果创建一个临时对象, 调用复制构造函数初始化r,然后销毁 临时返回值。C++0x中的移动

fredoverflow(用户237K代表)在他的两个答案中解释了大部分内容

但是,在实现Move构造函数和重载Move赋值操作符(
OMAO
)的过程中(我在整个问题中使用了这些简短的形式),我面临一些问题,我将在这里提出

另外还有一个由用户Greg Hewgill(代表826K的用户)给出的答案
我引用他的话

假设你 有一个函数返回一个重要的对象然后是普通的C++ 编译器将为multiply()的结果创建一个临时对象, 调用复制构造函数初始化r,然后销毁 临时返回值。C++0x中的移动语义允许“移动” 构造函数”,通过复制其内容来初始化r,以及 然后丢弃临时值,而不必销毁它

我亦会就此提出质询

好的,现在我开始

代码 .cpp 我把几个主要函数和它们的输出放在这里,这样我就可以很容易地讨论这个问题

1_main 输出
  • 为什么它没有执行Move构造函数,但如果我注释掉了.cpp文件中的Move构造函数定义和.h文件中的声明,那么它会给出错误
    [error]没有匹配函数来调用'A::A(A)
    ,如果我使用这个
    a1=std::Move(make_A())然后移动构造函数调用,那么为什么会发生这种情况呢
  • 为什么make_a()函数中对象
    a
    的析构函数没有运行
  • 2_main() 输出
  • 现在,复制构造函数和析构函数为临时对象运行,临时对象是由于返回*this from Move重载=操作符函数而创建的。根据Greg Hewgill语句
    C++0x
    允许调用Move构造函数来初始化它,方法是复制它的内容,然后丢弃临时值而不必销毁它。我使用的是
    C++11
    ,但初始化仍然是通过创建临时对象、复制构造函数来完成的
  • 我不知道第二个析构函数在运行哪个对象
  • 三号干线 fredoverflow(用户237K代表)保留了Move重载操作符的返回类型
    A&
    ,但我认为这是错误的

    A make_A();
    int main()
    {
        A a1,a2;
        a2=a1=make_A();
        
        a1.display();
        a2.display();
        
    }
    A make_A()
    {
        A a(2,"bonapart");
        return a;
        
    }
    
    输出 所以我觉得返回类型应该是
    A&&
    A
    ,但是
    A&&
    也会给出错误
    [error]不能将左值绑定到&&

    所以返回类型必须是
    A
    ,对吗

    4.
    在Move构造函数和Move重载=运算符中,我使用了
    a.s=nullptr
    此语句通常用于移动语义,但Overflow(用户)解释了“现在源不再拥有它所拥有的对象”之类的内容,但我没有得到它。因为如果我不写这句话,一切都会好起来的。请解释这一点

    1\u main:Move构造函数由于复制省略而未执行 交换完成后,观察到额外的析构函数,临时对象被销毁

    2_main:与调用移动运算符的1_main中的观察结果相同

    3_main:看到错误是因为您使用的是低版本的编译器。可能需要指定-std=c++11

    4:
    a.s=nullptr
    不是移动,因为您正在分配新内存并进行某种复制。比如说

    A::A(A &&a)                                   // Move ctor
    {
        std::cout<<"Move constructor\n";       
        p=a.p;
        s=a.s;
        a.s=nullptr; 
        a.p=0;
    }
    
    A::A(A&&A)//移动
    {
    
    std::cout您的类
    A
    有几个问题:

    • 您的分配操作员不处理自分配和泄漏:

      A& A::operator=(const A& a)
      {
          std::cout<<"overload operator=\n";
          if (this != &a)
          {
              p = a.p;
              delete[] s;
              s = new char[strlen(a.s) + 1];
              strcpy(s, a.s);
          }
          return *this;
      }
      
      由于潜在的拷贝省略(NRVO),有几种情况 (gcc有标记为
      -fno elide构造函数来控制该标记)

      如果NRVO适用,则
      a
      是“就地”构造,因此不会发生额外的破坏/移动

      否则会有一个移动构造函数和
      a
      的销毁

      A make_A()
      {
          A a(2,"bonapart"); // #2 ctor(int const char*)
          return a; // #3 move (elided with NRVO)
      } // #4 destruction of a, (elided with NRVO)
      
      int main()
      {
          A a1; // #1: default ctor
          a1 = // #5: Move assignment (done after make_A)
            make_A(); // #6: destructor of temporary create by make_A
      
          
          a1.display();
      } // #8: destructor of a1
      
      与NRVO

      default ctor
      ctor(int const char*)
      move assignment
      destructor
      display
      destructor
      
      无NRVO(
      -fno elide构造函数

      为了

      a1=make_A();
      使用移动分配。
      a2=(a1=make_A())
      使用复制分配作为移动分配返回(正确)
      A&

      四, 在Move constructor和Move-overloaded=operator中,我使用了
      a.s=nullptr;
      此语句总是用于Move语义中,而overflow(用户)解释了类似“现在源不再拥有它所拥有的对象”的内容但我不明白。因为如果我不写这句话,一切都很好。请解释这一点

      你的问题是你做的是复制而不是移动

      如果您使用
      s=a.s;
      而不是副本

      s = new char[strlen(a.s) + 1];
      strcpy(s, a.s);
      
      然后
      this->s
      a.s
      将指向相同的数据,并且
      this
      a
      将释放析构函数中的(相同)内存->双重释放错误


      a.s=nullptr;
      将解决该问题。

      在“移动”构造函数和赋值运算符中,您有一条语句
      a.s=nullptr;
      。这将导致泄漏,因为为
      a
      分配的内存不再可用。您应该“移动”指针,而不是重新分配和复制(复制构造函数和赋值运算符就是这样做的)。例如,只需交换指针:
      std::swap(s,a.s);
      (但请记住在构造函数中首先将
      s
      初始化为
      nullptr
      ),您想要一个新的还是一个答案?;)请不要在所有位置使用缩写1_由于复制省略,主移动构造函数未执行。请尝试
      a1(std::Move(make_A());
      a1=std::Move(make_A())
      要明确地称呼它,你应该在没有序言的情况下提出你的问题,对其他问题含糊其辞,每个问题回答一个问题。还有,第一个“子问题”是一个重复的谢谢,我理解你的答案,这将需要一些时间来理解,我必须学习一些新的点,你在你的答案中提到。我提到这个评论,以了解你,我正在检查答案,但不是忽视。好的。只是一个说明
      A make_A();
      int main()
      {
          A a1,a2;
          a2=a1=make_A();
          
          a1.display();
          a2.display();
          
      }
      A make_A()
      {
          A a(2,"bonapart");
          return a;
          
      }
      
      [Error] prototype for 'A& A::operator=(A&&)' does not match any in class 'A'
      
      A::A(A &&a)                                   // Move ctor
      {
          std::cout<<"Move constructor\n";       
          p=a.p;
          s=a.s;
          a.s=nullptr; 
          a.p=0;
      }
      
      A& A::operator=(const A& a)
      {
          std::cout<<"overload operator=\n";
          if (this != &a)
          {
              p = a.p;
              delete[] s;
              s = new char[strlen(a.s) + 1];
              strcpy(s, a.s);
          }
          return *this;
      }
      
      A::A(A&& a) : p(a.p), s(a.s)
      {
          a.s = nullptr;
          std::cout << "Move constructor\n";
      }
      
      A& A::operator=(A&& a)
      {
          std::cout << "Move overload operator=\n";
      
          if (this != &a) {
              p = a.p;
              delete [] s;
              s = a.s;
              a.s = nullptr;
          }
          return *this;
      }
      
      A make_A()
      {
          A a(2,"bonapart"); // Constructor
          return a;
      }
      
      A make_A()
      {
          A a(2,"bonapart"); // #2 ctor(int const char*)
          return a; // #3 move (elided with NRVO)
      } // #4 destruction of a, (elided with NRVO)
      
      int main()
      {
          A a1; // #1: default ctor
          a1 = // #5: Move assignment (done after make_A)
            make_A(); // #6: destructor of temporary create by make_A
      
          
          a1.display();
      } // #8: destructor of a1
      
      default ctor
      ctor(int const char*)
      move assignment
      destructor
      display
      destructor
      
      default ctor
      ctor(int const char*)
      move ctor
      destructor
      move assignment
      destructor
      display
      destructor
      
      A a1,a2;
      a2 = a1 = make_A();
      
      s = new char[strlen(a.s) + 1];
      strcpy(s, a.s);