移动构造函数和赋值中的存储和加载指令数

时间:2018-01-20 20:59:37

标签: c++ move-semantics

答案here表示,对于任何拥有一个拥有指针的对象。

Move constructor: 1 load and 2 stores.

Move assignment: 2 loads and 2 stores.

我的问题是,这是如何运作的。我的想法

移动作业

MyClass& operator=(MyClass&& rhs);//lhs is this

首先必须加载lhsrhs。然后rhs存储到lhs。这占了两个负载和一个商店。 rhs是否仍然存回,即使没有更改,它是一个右值?

移动构造函数

MyClass(MyClass&& rhs);

此时只有rhs加载到寄存器中。然后将其存储到lhs。那是一个负载和一个商店。什么比?同样,我错过了一家商店,如上所述。

1 个答案:

答案 0 :(得分:2)

正如在具有单个拥有指针的类的优化案例的链接答案中所指出的那样:

移动构造函数

MyClass(MyClass&& rhs):
    ptr(rhs.ptr) {            // load rhs.ptr, store it in ptr
    rhs.ptr = nullptr;        // store nullptr in rhs.ptr
}
  1. 加载rhs指针值
  2. 将其存储在新对象中
  3. nullptr存储在rhs对象
  4. 移动构造函数不需要加载当前指针,因为它通常是nullptr

    移动作业

    MyClass& operator=(MyClass&& rhs) {
        auto r3 = this->ptr;       // load this->ptr
        this->ptr = rhs.ptr;       // load rhs.ptr, store it in ptr
        rhs.ptr = r3;              // store rhs.ptr
        return *this;
    }
    
    1. 加载当前指针值
    2. 加载rhs指针值
    3. 将rhs存储在当前对象中
    4. 将来自第1步的旧值存储在rhs,move-from对象中,从而转移所有权并确保将其删除。
    5. 所以移动任务看起来就像交换。没有自我分配检查以防止分支。

      赋值必须确保释放指针前一个值指向的内存,并且源对象处于有效状态。确保这一点的最简单方法是将旧指针存储在rhs对象中,它将删除它。

      在这里,您可以看到由clang 5.0.0(https://godbolt.org/g/WxGxZ4)生成的稍微优化的代码:

      MyClass::MyClass(MyClass&&): # @MyClass::MyClass(MyClass&&)
        mov rax, qword ptr [rsi]
        mov qword ptr [rdi], rax
        mov qword ptr [rsi], 0
        ret
      MyClass::operator=(MyClass&&): # @MyClass::operator=(MyClass&&)
        mov rax, qword ptr [rdi]
        mov rcx, qword ptr [rsi]
        mov qword ptr [rdi], rcx
        mov qword ptr [rsi], rax
        mov rax, rdi
        ret