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

对象移动

C++11引入了对象移动而非拷贝的概念,有时候对象发生拷贝后就被销毁了,这种情况下移动而非拷贝对象会大幅度提升性能。

移动构造函数

移动构造函数类似于拷贝构造函数,不同的是移动构造函数的第一个参数是一个右值引用,移动构造函数仅仅移动数据成员,不会分配新的内存,所以比拷贝构造函数性能更好。

移动赋值运算符

移动赋值运算符与拷贝赋值运算符的关系和移动构造函数与拷贝构造函数的关系一样,第一个参数是一个右值引用,移动赋值运算符仅仅移动数据成员,不会分配新的内存。

在自定义移动赋值运算符时,需要检查是否存在自赋值,也就是说如果要赋值的对象与自己的地址一样,则不需要做任何事情。

移动后源对象必须是有效的,可析构的

移动操作必须确保移动后源对象可以被销毁且销毁后不会影响新创建的对象,例如如果源对象中有数据成员是指针,则必须置为空,否则在源对象执行析构函数时,会将新创建对象中的指针指向的资源释放掉。

移动操作还必须保证对象仍然可以安全地为其赋予新值或者可以安全地使用而不依赖其当前值。另一方面,移动操作对移动后源对象中留下的值没有任何要求,因此我们的程序不应该依赖于移动后源对象中的数据。

合成的移动操作

与处理拷贝构造函数和拷贝赋值运算符一样,编译器也会合成移动构造函数和移动赋值运算符,如果一个类定义了自己的拷贝构造函数,拷贝赋值运算符或析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符。

当一个类没有定义任何自己版本的拷贝构造函数,拷贝赋值运算符,析构函数,且类的每个非静态数据成员都可以移动时,编译器才会为它合成移动构造函数或移动赋值运算符。

定义了一个移动构造函数或移动赋值运算符的类必须也定义自己的拷贝构造函数和拷贝赋值运算符,否则拷贝构造函数和拷贝赋值运算符会被定义为删除的。

如果我们使用=default显式要求编译器生成合成的移动操作,且编译器不能移动所有成员,则编译器会将移动操作定义为删除的函数。

什么时候将合成的移动操作定义为删除的函数遵循与定义删除的合成拷贝操作类似的原则。

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

由于移动操作窃取资源,它通常不分配任何资源,因此,移动操作通常不会抛出任何异常。不抛出异常的移动构造函数和移动赋值运算符必须标记为noexcept,因为某些标准库容器除非知道移动操作是无异常的,否则就会进行拷贝。

class Person
{
public:
    const char*  name_;
    Person(const char* name):name_(name) {};

    Person(const Person& person)
    {
        name_ = person.name_;
        std::cout << "拷贝构造函数" << std::endl;
    }

    Person(const Person&& person)
    {
        name_ = person.name_;
        std::cout << "移动构造函数"<< std::endl;
    }

    Person& operator=(const Person& person)
    {

        name_ = person.name_;
        std::cout << "拷贝赋值运算符" << std::endl;
        return *this;
    }


    Person& operator=(Person&& person)
    {
        if (this != &person) {
            name_ = person.name_;
            std::cout << "移动赋值运算符" << std::endl;
        }
        return *this;
    }
};
 

 
Person getPerson()
{
    Person person("person in func");
    return person;
}

int main(void)
{
    Person person1("xiaohong");
    Person person2("xiaoming");
    Person person3(getPerson());//移动构造函数
    Person person4(std::move(person1));//移动构造函数
    Person person5(person2);//拷贝构造函数

    Person person6("xiaolan");
    person6 = std::move(person3);//移动赋值运算
    Person person7("xiaoqi");
    person7 = Person("xiaoqi");//移动赋值运算
    Person person8("xiaoba");
    person8 = person1;//拷贝赋值运算符
    system("pause");
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容