c++11 移动语义 移动构造函数 移动赋值运算符

创建日期: 2024-10-18 16:58 | 作者: 风波 | 浏览次数: 16 | 分类: C++

来源:https://blog.csdn.net/hubing_hust/article/details/128651426

移动构造函数和移动赋值运算符

class StrVec
{
public:
    StrVec(StrVec&&) noexcept;  // 移动构造函数
    // 其他成员函数定义

private:
    std::string* elements;      // 指向数组首元素的指针
    std::string* first_free;    // 指向数组的第一个空闲元素的指针
    std::string* cap;           // 指向数组尾后位置的指针
};

StrVec::StrVec(StrVec&& s) noexcept :   // 移动操作不应抛出任何异常
    // 成员初始化器接管s中的资源
    elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    // 令s进入这样的状态,对其运行析构函数是安全的
    s.elements = s.first_free = s.cap = nullptr;
}

移动赋值运算符

StrVec& StrVec::operator=(StrVec&& rhs) noexcept
{
    // 直接检测自赋值
    if (this != &rhs)
    {
        free();                         // 释放已有元素
        elements = rhs.elements;        // 从rhs中接管资源
        first_free = rhs.first_free;
        cap = rhs.cap;

        // 将rhs置为可析构的状态
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}

移动操作、标准库容器和异常

由于移动操作“窃取”资源,它通常不分配任何资源。因此,移动操作通常不会抛出任何异常。当编写一个不抛出异常的移动操作时,我们应该将此通知给标准库。否则,它会认为移动我们的类对象时可能会抛出异常,并且为了处理这种可能性而做出一些额外的工作。

一种通知标准库的方法是在我们的构造函数中指明 noexcept。 这是我们承诺一个函数不抛出异常的一种方法。并且,我们必须在类的头文件的声明中以及它的定义中(如果定义在类外的话),都指定noexcept。

移动一个对象通常会改变它的值。如果重新分配过程使用了移动构造函数,且在移动了部分而不是全部元素后抛出了一个异常,就会产生问题。旧空间中的移动源元素已经被改变了,而新空间中未构造的元素可能尚不存在。在此情况下,vector将不能满足自身保持不变的要求。

另一方面,如果vector使用了拷贝构造函数且发生了异常,它可以很容易地满足要求,因为在新内存中构造元素时,旧元素保持不变,如果此时发生了异常,vector可以释放新分配的内存并返回,而其旧内存中原有的元素仍然存在。

为了避免这种潜在问题,除非vector知道元素类型的移动构造函数不会抛出异常,否则在重新分配内存的过程中,它就必须使用拷贝构造函数而不是移动构造函数。如果希望vector在重新分配内存这类情况下对我们自定义类型的对象进行移动而不是拷贝,就必须显式地告诉标准库,我们的移动构造函数可以安全使用。这一点就是通过将移动构造函数标记为 noexcep 来做到的

移动后的源对象必须可析构

从一个对象移动数据并不会销毁此对象,但有时在移动操作完成后,源对象会被销毁。因此,我们编写移动操作时,必须确保移动后的源对象进入一个可析构的状态。

16 浏览
9 爬虫
0 评论