站长统计app,wordpress下载 4.8,企业网站每天更新,网站的建设与颜色搭配C98的C语法中就有引用的语法#xff0c;而C11中新增了右值引用的特性#xff0c;C11学习之后称之前的引用为左值引用。无论左值引用还是右值引用#xff0c;都是给对象起别名。一、左值和右值左值是一个表示数据的表达式#xff08;如变量名或解引用的指针#xff09; int b 1; const int c b; *p 10; string s(111); s[0] X;右值也是一个表示数据的表达式要么是字面常量、要么是表达式求值程中创建的临时对象这些都是存储于寄存器中的变量等右值可以出现在赋值符号的右边但是不能出现在赋值符号的左边右值不能取地址。double x 1,y2.2; 10; x y; string(1111);//匿名对象左值与右值的核心区别在于能不能取地址。二、左值引用和右值引用左值引用其实就是给左值起别名Type r1x;右值引用其实就是给右值起别名Type rr1y左值引用不能直接引用右值但是const左值可以引用右值右值引用不能直接引用左值但是右值引用可以引用move左值move是库里的一个函数模板本质内部是进行强制类型转换。这里要注意的是变量表达式都是左值属性也就意味着一个右值被右值引用绑定后右值引用变量表达式的属性是左值。int main() { int* p new int(0); int b 1; const int c b; *p 10; string s(111); s[0] X; //左值引用 int r1 b; int* r2 p; int r3 *p; string r4 s; char r5 s[0]; cout c endl; cout (void*) s[0] endl; double x 1,y2.2; 10; x y; fmin(x, y); string(1111);//匿名对象 int rr1 10; double rr2 x y; double rr3 fmin(x, y); string rr4 string(1111); //左值引用不能直接引用右值但是const左值引用可以引用右值 const int rx1 10; const double rx2 x y; //右值引用不能直接引用左值但是右值引用可以引用move左值 int rrx1 move(b); int rrx2 move(*p); string rrx3 move(s); // b、r1、rr1都是变量表达式都是左值 cout b endl; cout r1 endl; cout rr1 endl; //这里要注意 rr1的属性是左值所以不能再被右值引用绑定一下除非move一下 int rr6 move(rr1); return 0; }注左值引用和右值引用的最终目的是减少拷贝、提高效率左值引用还可以修改参数或返回值来方便使用。但是左值引用也是存在不足的部分函数返回场景只能传值返回不能左值引用返回。例如当前函数局部对象出了当前函数作用域生命周期就到了就销毁了不能用左值引用返回只能传值返回。三、引用延长生命周期右值引用可用于为临时对象延长生命周期const的左值引用也能延长临时对象生存期但是对象无法修改。int main() { std::string s1 zihan; const std::string r2 s1 s1; //不能通过const引用修改 r2zihan std::string r3 s1 s1; r3 zihan; std::cout r3 \n; return 0; }注这里是从一行延长到一个域而不是从一个栈帧延长到另一个栈帧。四、左值和右值的参数匹配在C98中实现一个const左值引用作为参数的函数那么实参传递左值和右值都可以匹配。C11以后分别重载左值引用、const左值引用、右值引用作为形参的f函数那么实参是左值会匹配f左值引用实参是const左值会匹配fconst左值引用实参是右值会匹配f右值引用。右值引用变量在用于表达式时属性是左值。五、左值引用主要场景左值引用主要使用场景是在函数中左值引用传参和左值引用传返回值时减少拷贝同时还可以修改实参和修改返回对象的价值。左值引用已经解决大多数场景的拷贝效率问题但是有些场景不能使用传左值引用返回如addStrings和generate函数C98的解决方案时被迫使用输出型参数解决。C11以后这里也不能用传右值引用返回因为这里的本质是返回对象是一个局部对象函数结束这个对象就析构销毁了栈帧直接销毁栈帧上的一个对象也会被直接销毁。六、右值引用主要场景移动构造和移动赋值移动构造函数是一种构造函数类似拷贝构造函数移动构造函数要求第一个参数是该类类型的引用但是不同的是要求这个参数是右值引用如果还有其他参数额外的参数必须有缺省值。窃取别人的资源来构造自己移动赋值是一个赋值运算符重载它跟拷贝赋值构成函数重载类似拷贝赋值函数移动赋值函数要求第一个参数是该类类型的引用但是不同的是要求这个参数是右值引用。交换右值的资源不用再进行深拷贝对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类移动构造和移动赋值才有意义因为移动构造和移动赋值的第一个参数都是右值引用的类型它的本质是“窃取”引用的右值对象的资源而不是像拷贝构造和拷贝赋值那样去拷贝资源从而提高了效率。下面自己实现一个string类来帮助理解namespace zihan { //实现string class string { public: typedef char* iterator; typedef const char* const_iterator; iterator begin() { return _str; } iterator end() { return _str_size; } const_iterator begin()const { return _str; } const_iterator end()const { return _str _size; } //构造 string(const char* str ) :_size(strlen(str)) , _capacity(_size) { cout string(char* str)-构造 endl; _str new char[_capacity 1]; strcpy(_str, str); } //交换 void swap(string s) { ::swap(_str, s._str); ::swap(_capacity, s._capacity); ::swap(_size, s._size); } //拷贝构造 string(const string s) :_str(nullptr) { cout string(const string s)-拷贝构造 endl; reserve(s._capacity); for (auto ch : s) { push_back(ch); } } void reserve(size_t n) { if (n _capacity) { char* tmp new char[n 1]; if (_str) { strcpy(tmp, _str); delete[] _str; } _str tmp; _capacity n; } } void push_back(char ch) { if (_size _capacity) { size_t newcapacity _capacity 0 ? 4 : _capacity * 2; reserve(newcapacity); }_str[_size] ch; _size; _str[_size] \0; } //移动构造 string(string s) { cout string(string s)-移动构造 endl; swap(s); } //拷贝赋值 string operator(const string s) { cout string operator(const string s)-拷贝赋值 endl; if (this ! s) { _str[0] \0; _size 0; reserve(s._capacity); for (auto ch : s) { push_back(ch); } } return *this; } //移动赋值 string operator(string s) { cout string operator(string s)-移动赋值 endl; swap(s); return *this; } ~string() { cout ~string()-析构 endl; delete[] _str; _str nullptr; } char operator[](size_t pos) { assert(pos _size); return _str[pos]; } string operator(char ch) { push_back(ch); return *this; } const char* c_str()const { return _str; } size_t size()const { return _size; } private: char* _str nullptr; size_t _size 0; size_t _capacity 0; }; }结语右值一般是临时对象是将亡值所以我们就可以在类中专门写一个供右值对象使用的拷贝构造和赋值重载函数来置换它们的资源从而减少不必要的深拷贝我们将这两个函数称为移动构造和移动赋值。