C++ 11:移动构造函数(move constructors)
移动构造函数
类 T 的移动构造函数是非模板构造函数,它的首个形参是 T&&、const T&&、volatile T&& 或 const volatile T&&,且没有其他形参,或剩余形参均有默认值。
1 |
|
移动语义和完美转发
左值/右值(两种判断方法)
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 (关闭优化)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Vinda's Blog!
评论