31 October 2022

C++ 核心指南目录

C.65: Make move assignment safe for self-assignment

理由

如果x = x会改变 x 的值,人们会觉得震惊,会引起出错。虽然,人们通常不会直接通过移动( move )来写自赋值操作,但是有些情况会使用移动操作。比如std::swap是通过移动操作实现的,如果你在处理swap(a, b)的时候,不小心让 ab 指向了同一个对象。这时候,错误的自移动操作会导致很难发现的严重故障。

例子

class Foo {
    string s;
    int i;
public:
    Foo& operator=(Foo&& a);
    // ...
};

Foo& Foo::operator=(Foo&& a) noexcept  // OK, but there is a cost
{
    if (this == &a) return *this;  // this line is redundant
    s = std::move(a.s);
    i = a.i;
    return *this;
}

这一行语句if (this == &a) return *this;并没太大作用,反而会使得程序变慢。

注意

ISO 标准只保障标准库容器的状态是“有效的状态,但不明确指定是什么状态”。显然,在十多年的实验和产品使用过程中,不是什么大问题。这条指南更强调完全的安全。

例子

这个例子展示了不需要进行测试的情况下,移动一个指针。可以认为是 move 操作的一个实现方法:

// move from other.ptr to this->ptr
T* temp = other.ptr;
other.ptr = nullptr;
delete ptr;
ptr = temp;

强化

  • (适度)在自赋值的情况下,移动赋值操作不能删除对象的指针成员,或把它设置成 nullptr
  • (不是强制的)查看标准库容器类型的使用方法,包括 string 。确保在普通的使用过程中是安全的。