C++ 右值引用和移动语义

左值

左值:一个表示数据的表达式(如变量名或者解除引用的指针),程序可从堆栈上获取其地址。最初,左值可出现在赋值语句的左边,但是在有了const修饰符后,可以声明一个没有赋值的标识符并获取其地址。

//左值的例子
int n;
int *pt = new int;
const int b = 101;
int & rn = n;
int & rt = *pt;
const int & rb = b;

int &var = 10; // error
const int &var = 10; // right  (const修饰符后,声明一个没有赋值的标识符并获取其地址)

int func(){
  int a = 10;
  return a;
}
int &var1 = func(); // error
cosnt int &var1 = func();  // right (但我们想要之后修改var1的值就无法修改了)

左值引用要求右边的值必须能够取地址,如果无法取地址,可以用常引用;如最后两个例子所示;
但使用常引用后,我们只能通过引用来读取数据,无法去修改数据,因为其被const修饰成常量引用了。
故C++11新增了右值引用;

右值

右值包括字面常量(c-风格字符串除外,他表示地址)、诸如x+y等表达式以及函数的返回值(函数的返回值不是引用);

int x = 10;
int y = 23;
int &&r1 = 13;
int &&r2 = x+y;  // (r2关联到的是23,即使后来修改了x、y,也不会使r2变化)
double &&r3 = std::sqrt(2.0);

移动语义

vector<int> getVector(int num){
  return vector<int>(num,0);
}

int main(){
  vector<int> res = getVector(1000); // 移动语义
}

在上面一个例子,其实已经使用到了移动语义vector<int>(vector<int> &&v)
但如果我们只考虑普通的拷贝构造函数,那么就需要将这个数组在返回值(指的是返回的临时对象而非res)的基础上在拷贝一份,然后再将返回值抛弃,做了大量的无用功;那么我们为何不直接把返回值中的数据的所有权直接移动到res中。
要实现移动语义,需要采取某种方式,让编译器知道什么时候需要赋值,什么时候不需要,而右值引用此时发挥了作用。

class U{
public:
  U(const U& f);  // 拷贝构造函数
  U(U && f);   // 移动构造函数

  U &operator=(const U& f)const;   // 拷贝赋值
  U &operator=(U &&f);            // 移动赋值
  U operator+(const U&f)const;
};

int main(){
  U u1;
  U u2 = u1;  //拷贝构造函数
  U u3(u1+u2);  //移动构造函数 
}

强制移动

通常移动构造函数和移动运算符使用右值,但一定要他们使用左值的时候,可以使用std::move()

// 如我们需要在一个候选数组中挑一个对象使用,并丢弃数组;
U choices[10];
U best;
ini pick;
// ... 进行选择, 将pick设置为目标索引
best = std::move(choices[pick]);

特殊的成员函数

C++11中特殊的成员函数有6个

  1. 默认构造函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
  4. 移动构造函数
  5. 移动复制运算符
  6. 析构函数

可以使用default设置默认方法和使用delete禁用方法

class Someclass{
public:
  Someclass()=default;  // 使用编译器给定的默认构造
  Someclass(const Someclass &)=delete;  // 禁止复制对象
  Someclass & operator=(const Somelcass &)=delete;  // 禁止复制对象
...
};
posted @   fwx  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
点击右上角即可分享
微信分享提示