移动语义和右值引用
案例:
vector<string> allcps(const vector<string>& vs) { vector<string> temp; ...//存储vs中的所有的值 return temp; } vector<string> vstr; vector<string> vstr_copy1(vstr);//#1 vector<string> vstr_copy2(allcps(vstr));//#2
vstr_copy2会先调用allcps函数,首先产生一个temp变量(allcps内部),然后生成一个临时返回对象并删除temp变量,再调用拷贝构造,最后再删除临时返回对象。
如果不删除原来的字符,仅仅是修改一下指向,这种方法叫做移动语义。
要实现移动语义,就要让编译器知道何时需要复制何时不需要,所以就要使用右值引用。
移动构造函数
class Test { private: char* pc; public: Test(); Test(const Test & t); Test(Test && t);//移动构造函数。因为要更改t,所以不能加上const ~Test(); Test operator+(const Test& t)const; } Test::Test() { pc = new char[100]; for(int i =0;i<100;++i){pc[i]=i;} } Test::Test(const Test& t) { pc = new char[100]; for(int i=0;i<100;++i){pc[i]=t.pc[i];} } Test::Test(Test&& t) { pc=t.pc; t.pc=nullptr;//pc和t.pc指向同一块内存,调用析构时可能会出现问题,不能对同一块地址delete两次 //所以将t.pc设置为空地址,因为delete空指针是可以的 } Test::~Test() { delete [] pc; } Test Test::operator+(const Test& t) const { Test temp ; for (int i = 0; i < 100; ++i) { temp.pc[i] = this->pc[i]+t.pc[i]; } return temp; } int main() { Test one; Test two = one;//调用普通的拷贝构造。因为one是左值 Test three; Test four(one+three);//调用移动构造函数。因为one+three为右值 } //在c++98没有引入右值引用时,如果实参为右值,const引用形参将执行一个临时变量
赋值
适用于构造函数的移动语义也适用于赋值运算符
Test& Test::operator=(const Test& t) { if(this == &t) return *this; delete [] pc; pc = new char[100]; for(int i=0;i<100;i++) { pc[i]=t.pc[i]; } return *this; } Test& Test::operator=(Test&& t) //同样没有const修饰参数,因为要改动 { if(this == &t) return *this; delete [] pc; pc = t.pc; t.pc = nullptr; return *this; }
强制移动
移动构造函数和移动赋值运算符号都是使用右值,如果只有左值怎么办?
可以使用
static_cast<>
将对象的类型强行转为Test &&
使用utility头文件中的std::move()
Test four = std::move(one)
std::move并非一定会使用移动操作,例如:
Chunk one; ... Chunk two; two = std::move(one);//如果定义了移动赋值运算符,这样没有问题 //如果没有定义移动赋值运算符,将使用复制赋值运算符,如果也没有定义,那将不允许