C++11中的左右值引用(略带复习)
引子:我们在之前学过,auto(底层为迭代器),范围for,{}初始化(可以省略=),initializer_list,decltype,nullptr(Null字面上为0),arrray(与数组相比,对于出错的处理不同),forward_list(与普通的相比少一点空间),还学过unordered_map与unordered_set(底层为哈希表,详见我前一篇封装)新容器等内容,这些其实是C++11新增的内容,今天我们就来讲一讲另一个C++11的内容——左右值的引用!
一,初解:何为左值,何为右值?
左值(Lvalue)和右值(Rvalue)是表达式的两种分类。
左值指的是可以出现在赋值表达式的左边的表达式,它表示一个对象的身份,即内存地址。
右值则是不能出现在赋值表达式的左边的表达式,它表示一个临时的值或者一个无法获取地址的表达式。
左值引用就是给左值的引用,给左值取别名。----&
右值引用就是对右值的引用,给右值取别名----&&
为什么用&&来右值引用?
我认为:
一,通过使用右值引用,可以确保只有对象的值被移动,而不是对象的引用或指针
二,临时对象通常是右值,它们在表达式中产生并立即被使用。使用&&
可以高效地利用这些临时对象,通过移动它们的资源而不是进行复制
举例:左右值:如下
// 左值
int* p = new int(0);
int b = 1;
const int c = 2;
double x = 1.1, y = 2.2;// 右值
10;
x + y;
fmin(x, y);
举例:左右值引用:如下
// 左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;// 右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
二,再解:左右值引用的转换。
左值引用总结: 1. 左值引用只能引用左值,不能引用右值。 2. 但是const左值引用既可引用左值,也可引用右值
右值引用总结: 1. 右值引用只能右值,不能引用左值。 2. 但是右值引用可以move以后的左值。
move是什么?
其实也不用太复杂!我认为就是一个强转!就是转成右值!
补:其实在底层上,都开了空间,但是在语法上不显示
引用转换:
//左转右
int h ;//左值
int& d = 10;//左值引用,,错误
double& n =x+y;//左值引用,,错误
double& j = fmin(x, y);//左值引用,错误
const int& a = 10;//左值引用
const double& k = x+y;//左值引用
const double&l = fmin(x,y);//左值引用//右转左
int g=10;
int&& t =g;//右值引用,错误
int&& t1 = move(g); //右值引用
如上图错误:
四,后解:右值引用的应用场景与作用!
主要有以下几种:
一,临时对象:例如函数返回的值,它们是右值。
二,移动语义:通过std::move
将资源从一个地方移动到另一个地方,避免复制的开销。
移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不 用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己
三,右值引用:允许以右值引用的方式访问右值,这在现代C++编程中用于实现资源的高效转移。
四,完美转发:在模板编程中,完美转发允许将参数以左值或右值的形式传递给其他函数,保持其值类别
我认为右值引用是为了弥补左值引用无法解决传值的问题,可以节省一个拷贝构造!
参考代码:
class Resource {
public:void swap(Resource& s){std::swap(size, s.size);std::swap(data, s.data);}Resource(int size) : size(size), data(new int[size]) {cout << "初始构造" << endl;}// 移动构造函数Resource(Resource&& other) : size(0), data(nullptr) {cout << "移动构造" << endl;swap(other);}// 拷贝构造函数Resource(const Resource& s){cout << "拷贝构造函数" << endl;Resource cmp(s.size);swap(cmp);}// 移动赋值运算符Resource& operator=(Resource&& other){if (this != &other) {cout << "移动赋值运算" << endl;swap(other);}return *this;}// 拷贝赋值运算Resource& operator=(const Resource& s){cout << "拷贝赋值" << endl;Resource cmp(s.size);swap(cmp);}~Resource() {delete[] data;}private:int* data;int size;
};int main()
{Resource h1(3);Resource h2(h1);Resource h3(move(h1));Resource h4 =move(h2);return 0;
}
补:其实右值引用的本质上还是左值!
五,万能的转发(补)
万能的转发是指在模板函数中使用std::forward
来转发参数,它可以保持参数的原始值类别(左值或右值)。这在模板编程中非常有用,因为它允许函数模板接受不同类型的参数,并将这些参数以它们原始的值类别传递给其他函数
测试代码:
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }/*引用传左值->左值引用传右值->右值引用*/
template<typename T>
void PerfectForward(T&& t)
{// 模版实例化是左值引用,保持属性直接传参给Fun// 模版实例化是右值引用,右值引用属性会退化成左值,转换成右值属性再传参给FunFun(forward<T>(t));
}int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}
结果:
下集:我们进入可变参数的摸板分享!