大气医院网站模板,餐饮招商加盟网站建设,网页设计工资一般多少钱一小时,公司网页制简要介绍
右值引用是C11的新特性,无论左值引用还是右值引用#xff0c;都是在给对象取别名
什么是左值 什么是右值
1.左值,左值引用
左值是一个数据的表达式(例如变量或者解引用后的指针),我们可以对其进行取地址和修改赋值,左值可以出现在赋值符号的左边,而右值不能出现在…简要介绍
右值引用是C11的新特性,无论左值引用还是右值引用都是在给对象取别名
什么是左值 什么是右值
1.左值,左值引用
左值是一个数据的表达式(例如变量或者解引用后的指针),我们可以对其进行取地址和修改赋值,左值可以出现在赋值符号的左边,而右值不能出现在赋值符号的左边,而左值引用就是对左值起别名,
如图 2.右值
右值也是一个表示数据的表达式如字面常量、表达式返回值函数返回值(这个不能是左值引 用返回)等等右值可以出现在赋值符号的右边但是不能出现出现在赋值符号的左边右值不能 取地址。
右值引用
右值引用就是对右值的引用给右值取别名 这几个都是常见的右值引用
需要注意的是,在给右值引用后,变量就会变成左值,为什么? 因为需要对其进行修改,如果需要再要将其变成右值,需要加上std::move()将其变成右值
右值是不能取地址的但是给右值取别名后会导致右值被存储到特定位置且可 以取到该位置的地址也就是说例如不能取字面量10的地址但是a引用后可以对a取地 址也可以修改rr1。如果不想a被修改可以用const int a 去引用是不是感觉很神奇 这个了解一下实际中右值引用的使用场景并不在于此这个特性也不重要 左值引用和右值引用比较
左值引用总结
1. 左值引用只能引用左值不能引用右值。 2. 但是const左值引用既可引用左值也可引用右值 右值引用总结
1. 右值引用只能右值不能引用左值。 2. 但是右值引用可以move以后的左值。
为什么要有右值引用
从左值引用窥探
前面我们可以看到左值引用既可以引用左值和又可以引用右值那为什么C11还要提出右值引 用呢是不是画蛇添足呢下面我们来看看左值引用的短板右值引用是如何补齐这个短板的 注意以下采用右值引用的移动构造
#pragma once
class string
{
public:string(const char* str ):_size(strlen(str)), _capacity(_size){//cout string(char* str) endl;_str new char[_capacity 1];strcpy(_str, str);}// s1.swap(s2)void swap(string s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string s):_str(nullptr){cout string(const string s) -- 深拷贝 endl;string tmp(s._str);swap(tmp);}// 赋值重载string operator(const string s){cout string operator(string s) -- 深拷贝 endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string s):_str(nullptr), _size(0), _capacity(0){cout string(string s) -- 移动语义 endl;swap(s);}// 移动赋值string operator(string s){cout string operator(string s) -- 移动语义 endl;swap(s);return *this;}~string(){delete[] _str;_str nullptr;}char operator[](size_t pos){assert(pos _size);return _str[pos];}void reserve(size_t n){if (n _capacity){char* tmp new char[n 1];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 operator(char ch)string operator(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}
private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0
};左值引用的使用场景做参数和做返回值都可以提高效率
如下:
void func1(string s1)
{}
void func2(string s1)
{}
int main()
{string s1(hello world);// func1和func2的调用我们可以看到左值引用做参数减少了拷贝提高效率的使用场景和价值func1(s1);func2(s1);// string operator(char ch) 传值返回存在深拷贝// string operator(char ch) 传左值引用没有拷贝提高了效率s1 !;return 0;
}
左值引用的短板 但是当函数返回对象是一个局部变量出了函数作用域就不存在了就不能使用左值引用返回 只能传值返回。例如string to_string(int value)函数中可以看到这里只能使用传值返回 传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。 这里可以看到,传值返回会至少导致一次拷贝构造(旧编译可能优化没有那么好就是两次了)
因为to_string的返回值是右值,利用这个右值构造ret,如果没有了移动构造,就会去调用拷贝构造,就会给上一个深拷贝,可以看见,这里是有优化空间的
出来吧!我的右值引用和移动语义
解决方案:
在string中增加移动构造移动构造本质是将参数右值的资源窃取过来占位已有那么就不 用做深拷贝了所以它叫做移动构造就是窃取别人的资源来构造自己
string (string s):_str(nullptr)
{cout这是移动构造endl;swap(s);
}
string (const string s):_str(nullptr)
{cout拷贝构造深拷贝endl;string tmp._str;swap(tmp,s);
}
int main()
{string ret2 bit::to_string(-1234);return 0;
} 再运行一下,就会发现你会调用移动构造,而移动构造里没有开空间,进行拷贝数据,所以效率提高了
(当移动构造和拷贝构造同时出现的时候,因为to_string的返回值是右值,所以编译器就会选择适合的-移动构造)
同样的道理,移动赋值也是这样
string operator(string s)
{cout string operator(string s) -- 移动语义 endl;swap(s);return *this;
}
int main()
{string ret1;ret1 to_string(1234);return 0;
}// 运行结果 // string(string s) -- 这是移动构造 // string operator(string s) -- 移动语义 这里运行后我们看到调用了一次移动构造和一次移动赋值。因为如果是用一个已经存在的对象 接收编译器就没办法优化了,to_string函数中会先用str生成构造生成一个临时对象但是 我们可以看到编译器很聪明的在这里把str识别成了右值调用了移动构造。然后在把这个临时 对象做为to_string函数调用的返回值赋值给ret1这里调用的移动赋值
万能引用 完美转发
模板中的万能引用
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)//万能引用 传左值的时候就变成 int t 右值就是int
{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;
}
std::forward完美转发
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; }
// std::forwardT(t)在传参的过程中保持了t的原生类型属性。
templatetypename T
void PerfectForward(T t)
{Fun(std::forwardT(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;
}