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个:
- 默认构造函数
- 拷贝构造函数
- 拷贝赋值运算符
- 移动构造函数
- 移动复制运算符
- 析构函数
可以使用default设置默认方法和使用delete禁用方法
class Someclass{
public:
Someclass()=default; // 使用编译器给定的默认构造
Someclass(const Someclass &)=delete; // 禁止复制对象
Someclass & operator=(const Somelcass &)=delete; // 禁止复制对象
...
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界