C++中的移动构造函数和拷贝构造函数

935 阅读2分钟

移动构造函数和拷贝构造函数

为什么会有这两种构造函数呢?这个牵扯到语义上的一个操作,一个是拷贝,一个是移动,拷贝的意义在于我需要将当前这个对象拷贝出完全独立的一份数据出来,包括这个类的对象中的指针所指向的数据等。移动构造在于将当前这个对象的内容转移给另外一个对象(这里需要考虑一个变量生命周期的问题)。通过移动构造函数,可以将一个本来即将消亡的对象的内容继续延长生命周期到被拷贝的对象当中,减少拷贝次数。

拷贝构造函数

Test(const Test &test);

const Test &test 这个写法叫做常量左值引用,既可以接受左值,也可以接受右值,但是在语义上是左值,所以不会接受一个临时值。

移动构造函数

Test(Test &&test);

Test &&test 是 C++ 中的右值引用类型的声明(Rvalue Reference Type Declaration)。

在 C++11 中,引入了新的引用类型——右值引用(Rvalue Reference),表示一个能够绑定到右值表达式的引用。右值可以是临时变量或者没有名称的字面量。

当对象满足以下情况时,编译器会优先选择移动构造函数:

1、对象是右值,即将要消亡的临时对象或std::move()将左值转化为右值。

2、对象的资源是可移动的,如动态内存申请的指针。

3、对象的拷贝成本较高,而移动成本较低。

std::move()

但是会遇到一些左值,这些左值生命周期大于当前需要被拷贝的对象,并且数据可以共享,这个时候就可以使用std::move()把当前这个值转为右值进行移动语义操作。

完整例子

#include <iostream>
#include <utility>
class Test
{
    private:
    public:
        // 拷贝构造函数
        Test();
        Test(const Test &test);
        Test(Test &&test);
        ~Test();
};
Test::Test()
{
    std::cout << "初始化" << std::endl;
}
Test::Test(const Test &test)
{
    std::cout << "调用拷贝构造函数" << std::endl;
}
Test::Test(Test &&test)
{
    std::cout << "调用移动构造函数" << std::endl;
}
Test::~Test()
{
     std::cout << "调用析构函数" << std::endl;
}
Test get_test()
{
    Test test = Test();
    return test;
}

int main()
{
    // 初始化
    // 调用移动构造函数
    // 调用析构函数
    // 调用析构函数
    get_test();
    // 初始化
    // 调用移动构造函数
    // 调用析构函数
    // 调用析构函数
    Test test = get_test();
    // 调用拷贝构造函数
    Test test1 = test;
    // 调用移动构造函数 std::move将当前左值转换成将亡值
    Test test2 = std::move(test);
}