校园二手市场网站开发的意义,国家对地理信息网站建设的重视,创意设计网,在家开个人工作室违法吗文章目录 左值和右值什么是左值什么是右值左值引用与右值引用的比较左值引用总结右值引用的总结#xff1a; 右值引用使用场景和意义左值引用的使用场景左值引用的短板 右值引用和移动语义解决上面的问题不仅仅有移动构造还有移动赋值 右值引用引用左值及其一些更深入的使用场… 文章目录 左值和右值什么是左值什么是右值左值引用与右值引用的比较左值引用总结右值引用的总结 右值引用使用场景和意义左值引用的使用场景左值引用的短板 右值引用和移动语义解决上面的问题不仅仅有移动构造还有移动赋值 右值引用引用左值及其一些更深入的使用场景分析完美转发模板中的万能引用 左值和右值
什么是左值什么是右值
首先关于左值和右值我们的第一印象左边的是左值右边的是右值这里的左右值指的是等号的左右 但是实际上我们并不这样区分或者说这样子区分是不太准确的左值是一个表示数据的表达式比如说变量名和解引用的指针我们可以获取它的地址对它赋值左值可以出现赋值符号的左边也可以出现在赋值符号的右边而右值不能出现在赋值符号的左边 但是被const定义的左值不能出现在等于号的左边不能给他赋值但是可以取他的地址因此我们可以通过能否取地址来判断左右值。 可以取地址的是左值不可以取地址的是右值 右值可以是一个表达式比如说ab是一个右值也可以是一个常量比如说10 都是右值。右值引用就是对右值取别名。这里需要注意一个事情常量字符串是属于左值的
左值引用与右值引用的比较
左值引用总结
1.左值引用只能引用左值不能引用右值 2.但是const左值引用可以引用右值。
右值引用的总结
1.右值引用只能引用右值不能引用左值 2但是右值引用可以move以后的右值。
右值引用使用场景和意义
前面我们可以看到左值引用既可以引用左值和又可以引用右值那为什么要提出右值引用这里我们需要先从左值引用的短板说起。请看 以下代码这是我自己实现的string
#includestring.h
clzyf::string func()
{clzyf::string a ****************;return a;
}
int main()
{clzyf::string b func();clzyf::string a b;return 0;
}请大家看看这里需要拷贝多少次呢首先如果去掉构造函数的话我们这里的传值返回就需要先拷贝一个临时对象再将临时对象传给b然后在进行销毁但是我们运行程序的时候会发现这里程序进行了优化那么我们可能会疑惑为什么我们不吧这里定义成引用返回这里是因为这个函数作用域在接下来是会被销毁的那么这个对象也会被销毁当将其销毁后其地址也就会释放那么这里传引用也没用因为这个对象已经被销毁了。 我们可以发现这里运行程序也会崩溃所以这样字是肯定不行的。我们可以总结
左值引用的使用场景 做参数和做返回值都可以提高效率。 左值引用的短板 当函数返回对象是一个局部变量出了函数作用域就不存在了就不能使用左值引用返回 只能传值返回。例如bit::string to_string(int value)函数中可以看到这里只能使用传值返回 传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。 右值引用和移动语义解决上面的问题
在string中增加移动构造移动构造本质是将参数右值的资源窃取过来占位已有那么就不 用做深拷贝了所以它叫做移动构造就是窃取别人的资源来构造自己。 我们来给大家看一下代码。
string(string s):_str(nullptr)
,_size(0),_capacity(0)
{cout string(string s) -- 移动语义 endl;swap(s);
}在将这个代码加入进去后我们就可以发现再运行的时候这里没有调用深拷贝和拷贝构造而是调用了移动构造移动构造没有新开空间拷贝数据所以效率提高了。 这里为什么可以调用移动构造呢原因是因为内置类型在这里进行拷贝的时候它本身其实是一个将亡值那么作为一个将亡值它本身是要被销毁掉的因此它的地址是不可取的它属于右值那么就会调用右值引用的移动构造进行一个swap是的他的资源不被释放而是被交换给b而b的资源则有将亡值销毁的时候进行释放代码如下
#includestring.h
clzyf::string func()
{clzyf::string a ****************;return a;
}
int main()
{clzyf::string b;b func();return 0;
}不仅仅有移动构造还有移动赋值
string类中增加移动赋值函数再去调string(1234)不过这次是将string(1234)返回的右值对象赋值给ret1对象这时调用的是移动构造。 代码入下
clzyf::string to_string(int s)
{string str;return str;
}
// 移动赋值
string operator(string s)
{
cout string operator(string s) -- 移动语义 endl;
swap(s);
return *this;
}
int main()
{clzyf::string ret1;ret1 clzyf::string(1234);return 0;
}这里运行后我们可以看到进行了一次移动构造和一次移动赋值我们这里是先用to_string 返回一个右值然后通过这个右值调用移动赋值来给ret1进行赋值
右值引用引用左值及其一些更深入的使用场景分析
按照语法右值引用只能引用右值但右值引用一定不能引用左值吗因为有些场景下可能 真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时可以通过move 函数将左值转化为右值。C11中std::move()函数位于 头文件中该函数名字具有迷惑性 它并不搬移任何东西唯一的功能就是将一个左值强制转化为右值引用然后实现移动语义。
完美转发
模板中的万能引用
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; }
// 模板中的不代表右值引用而是万能引用其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力
// 但是引用类型的唯一作用就是限制了接收的类型后续使用中都退化成了左值
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
templatetypename T
void PerfectForward(T t)
{Fun(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;
}万能引用既可以接受左值也可以接受右值这里的原因是因为这里有个方式就是引用折叠他的格式是模板
templateclass T
void func(Tt)
{
return;
}此时这个小t就是一个万能引用万能引用当我们传入左值的时候会进行折叠从而接受左值当我们传入右值的时候不需要折叠本身就是右值因此既可以接受左值对象又可以接受右值