移动构造函数

类 T 的移动构造函数是非模板构造函数,它的首个形参是 T&&、const T&&、volatile T&& 或 const volatile T&&,且没有其他形参,或剩余形参均有默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <string>
#include <iostream>
#include <iomanip>
#include <utility>

struct A
{
std::string s;
int k;

A() : s("测试"), k(-1) {}
A(const A& o) : s(o.s), k(o.k) { std::cout << "移动失败!\n"; }
A(A&& o) noexcept :
s(std::move(o.s)), // 类类型成员的显式移动
k(std::exchange(o.k, 0)) // 非类类型成员的显式移动
{}
};

A f(A a)
{
return a;
}

struct B : A
{
std::string s2;
int n;
// 隐式移动构造函数 B::(B&&)
// 调用 A 的移动构造函数
// 调用 s2 的移动构造函数
// 并进行 n 的逐位复制
};

struct C : B
{
~C() {} // 析构函数阻止隐式移动构造函数 C::(C&&)
};

struct D : B
{
D() {}
~D() {} // 析构函数阻止隐式移动构造函数 D::(D&&)
D(D&&) = default; // 强制生成移动构造函数
};

int main()
{
std::cout << "尝试移动 A\n";
A a1 = f(A()); // 按值返回时,从函数形参移动构造其目标
std::cout << "移动前,a1.s = " << std::quoted(a1.s)
<< " a1.k = " << a1.k << '\n';
A a2 = std::move(a1); // 从亡值移动构造
std::cout << "移动后,a1.s = " << std::quoted(a1.s)
<< " a1.k = " << a1.k << '\n';

std::cout << "尝试移动 B\n";
B b1;
std::cout << "移动前,b1.s = " << std::quoted(b1.s) << "\n";
B b2 = std::move(b1); // 调用隐式移动构造函数
std::cout << "移动后,b1.s = " << std::quoted(b1.s) << "\n";

std::cout << "尝试移动 C\n";
C c1;
C c2 = std::move(c1); // 调用复制构造函数

std::cout << "尝试移动 D\n";
D d1;
D d2 = std::move(d1);
}

移动语义和完美转发

左值/右值(两种判断方法)
1.在赋值表达式中,出现在等号左边的就是左值,而在等号右边的则是右值。
2.可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。

移动语义
“偷走”临时变量中资源的构造函数,称为“移动构造函数”,“偷”的行为,则称为“移动语义”。

完美转发
在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。

#include <iostream>
using namespace std;

// 浅拷贝
class TestShollowCopy {
public:
    TestShollowCopy():data(new int(1)){}
    TestShollowCopy(const TestShollowCopy& other) :data(other.data) {}
    ~TestShollowCopy() { delete data; }

private:
    int* data;
};

// 深拷贝
class TestDeepCopy {
public:
    TestDeepCopy() :data(new int(1)) {}
    TestDeepCopy(const TestDeepCopy& other) :data(new int(*other.data)) {}
    ~TestDeepCopy() { delete data; }

private:
    int* data;
};



class TestCopy {
public:
    TestCopy() :data(new int(1)) {
        cout << "Default construct: " << ++nsc << endl; 
    }

    TestCopy(const TestCopy&other) :data(new int(*other.data)) {
        cout << "Copy construct: " << ++ndc << endl; 
    }
    // 移动构造函数
    TestCopy(TestCopy&& other) :data(other.data) {
        other.data = nullptr; // 将临时值的指针成员置空
        cout << "Move construct: " << ++nmc << endl;
    }

    ~TestCopy() { 
        delete data;
        cout << "Delete construct: " << ++nd << endl;
    }

//private:
    int* data;
    static int nsc;
    static int ndc;
    static int nmc;
    static int nd;
};

int TestCopy::nsc = 0;
int TestCopy::ndc = 0;
int TestCopy::nmc = 0;
int TestCopy::nd = 0;

TestCopy GetTemp() {
    TestCopy t;
    cout << __func__ << ": " << hex << t.data << endl;
    return t;
}


// Copyable 
struct Copyable {
    Copyable() {}
    Copyable(const Copyable& o) { cout << __func__ << endl; }
};

Copyable ReturnRvalue() { return Copyable(); }
void AcceptVal(Copyable) {}
void AcceptRef(const Copyable &) {}




// std::move
class Moveable {
public:
    Moveable() : i(new int(2)) {}
    ~Moveable() { delete i; }
    Moveable(const Moveable &o) : i(new int(*o.i)) {}
    Moveable(Moveable &&o) :i(o.i) {
        o.i = nullptr;
    }

    int *i;
};

Moveable GetTemp1() {
    Moveable tmp = Moveable();
    cout << __func__ << ": " << hex << tmp.i << endl;
    return tmp;
}


// move_if_noexcept 替代 move (牺牲性能保证安全的一种做法)
// 在类的移动构造函数没有 noexcept 关键字修饰时返回一个左值引用,从而使变量可以使用拷贝语义
// 而在类的移动构造函数有 noexcept 关键字时,返回一个右值引用,从而使变量可以使用移动语义
struct Maythrow {
    Maythrow() {}
    Maythrow(const Maythrow&) { cout << "Maythrow copy constructor." << endl; }
    Maythrow(Maythrow&&) { cout << "Maythrow move constructor." << endl; }
};

struct Nothrow {
    Nothrow() {}
    Nothrow(const Nothrow&) { cout << "Nothrow copy constructor." << endl; }
    Nothrow(Nothrow&&) noexcept { cout << "Nothrow move constructor." << endl; }
};



// 完美转发
void RunCode(int&& i) { cout << "rvalue ref" << endl; }
void RunCode(int& i) { cout << "lvalue ref" << endl; }
void RunCode(const int&& i) { cout << "const rvalue ref" << endl; }
void RunCode(const int& i) { cout << "const lvalue ref" << endl; }

// 完美转发的作用就是做包装函数
template<typename T>
void PerfectForward(T&& t) { RunCode(forward<T>(t)); }

void RunCode1(double&& m) {}
void RunHome(double&& m) {}
void RunComp(double&& m) {}

template<typename T, typename U>
void PerfectForward(T&& t, U& func)
{
    cout << t << "\tforwarded..." << endl;
    func(forward<T>(t));
}

int main()
{
    TestDeepCopy dc;
    TestDeepCopy dc1(dc);

    // dc.data 和 dc1.data 指向同一块堆内存,当析构其中之一后,另一个就成了“悬挂指针”,
    // 那么在该悬挂指针上释放内存就会造成严重的错误
    TestShollowCopy sc;
    //TestShollowCopy sc1(sc); // error


    TestCopy t = GetTemp();
    cout << __func__ << ": " << hex << t.data << endl;
    // t.data 和 GetTemp() 中的t.data地址是一样的,因为TestCopy类实现了移动构造函数

    // 
    // 禁止优化,才可以看到效果:g++ -std=c++11 C++11_rvalue_ref_move_semantics.cpp -fno-elide-constructors 
    cout << "pass by value: " << endl;
    AcceptVal(ReturnRvalue());
    cout << "pass by reference: " << endl;
    AcceptRef(ReturnRvalue());


    Moveable m;
    Moveable m1(move(m));
    // cout << *m.i << endl; // error: m.i被m1的移动构造函数设置为nullptr,由于m的生命周期为main()函数,那么调用*m.i的时候会发生严重的运行时错误
    cout << *m1.i << endl;
    Moveable m2(GetTemp1());


    Maythrow maythrorw;
    Maythrow mt = move_if_noexcept(maythrorw); // copy
    Nothrow nothrow;
    Nothrow nt = move_if_noexcept(nothrow); // move
    

    int i, j;
    const int ci = 1;
    const int cj = 0;

    PerfectForward(i);       // lvalue ref
    PerfectForward(move(j)); // rvalue ref
    PerfectForward(ci);      // const lvalue ref
    PerfectForward(move(cj));// const rvalue ref

    PerfectForward(1.2, RunCode1);
    PerfectForward(4, RunComp);
    PerfectForward(6.2, RunHome);

    return 0;
}

// g++ -std=c++11 C++11_rvalue_ref_move_semantics.cpp -fno-elide-constructors (关闭优化)