右值和左值的区分即右值引用的理解

左值与右值总结

左值与右值不是很好区分,一般认为:

普通类型的变量,因为有名字,可以取地址,都认为是左值

const修饰的常量,不可修改,只读类型的,理论上应该按照右值对待,但因为其可以取地址(如果只是const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间),C++11认为其是左值

如果表达式运行结果或单个变量是一个引用则认为是左值

如果表达式的运行结果是一个临时变量或者对象,认为是右值

C++11中的右值

右值引用,顾名思义就是对右值的引用。

C++11中,右值由两个概念组成:纯右值和将亡值。

纯右值

纯右值是C++98中右值的概念,用于识别临时变量和一些不跟对象关联的值。比如:常量、一些运算表达式(1+3)等

将亡值

声明周期将要结束的对象。比如:在值返回时的临时对象。

class String
{
public:
 String(char* str = "")
 {
 if (nullptr == str)
 str = "";
 _str = new char[strlen(str) + 1];
 strcpy(_str, str);
 }
 
 String(const String& s)
 : _str(new char[strlen(s._str) + 1])
 {
 strcpy(_str, s._str);
 }
 
 String& operator=(const String& s)
 {
 if (this != &s)
 {
 char* pTemp = new char[strlen(s._str) +1];
 strcpy(pTemp, s._str);
 delete[] _str;
 _str = pTemp;
 }
 return *this;
 }
 
 ~String()
 { if (_str) delete[] _str;}
private:
 char* _str;
};
//假设现在有一个函数,返回值为一个String类型的对象:

String GetString(char* pStr) {
 String strTemp(pStr);
 return strTemp; }

int main()
{
 String s1("hello");
 String s2(GetString("world"));
 return 0; }
GetString 函数返回的临时对象,将 s2 拷贝 构造成功之后,立马被销毁了 ( 临时对象的空间被释放 ) ,再没有其他作用;而 s2 在拷贝构造时,又需要分配 空间,一个刚释放一个又申请,有点多此一举
 
右值引用书写格式:
1.类型 && 引用变量名字 = 实体;
右值引用最长常见的一个使用地方就是:与移动语义结合,减少无必要资源的开辟来提高代码的运行效率。

String(String&& s): _str(s._str)
{ s._str = nullptr; } 
String&& GetString(char* pStr) {
 String strTemp(pStr);
 return strTemp; }
int main()
{
 String s1("hello");
 String s2(GetString("world"));
 return 0; }
2.右值引用另一个比较常见的地方是:给一个匿名对象取别名,延长匿名对象的声明周期。
  与引用一样, 右值引用在定义时必须初始化 。 通常情况下,右值引用不能引用左值

注意

1.与引用一样,右值引用在定义时必须初始化。 通常情况下右值引用不能引用左值

2.在C++11中,拷贝构造/移动构造/赋值/移动赋值函数必须同时提供,或者同时不提供,程序才能保证类同时具有拷贝和移动语义。

std::move()
并不搬移任何东西,唯一的功能 就是将一个左值强制转化为右值引用,通过右值引用使用该值,实现移动语义
 
注意:被转化的左值,其声 明周期并没有随着左右值的转化而改变,即std::move 转化的左值变量 lvalue 不会被销毁
class Person
{
public:
 Person(char* name, char* sex, int age)
 : _name(name)
 , _sex(sex)
 , _age(age)
 {}
 
 Person(const Person& p)
 : _name(p._name)
 , _sex(p._sex)
 , _age(p._age)
 {}
 
#if 0
 
 Person(Person&& p)
 : _name(p._name)
 , _sex(p._sex)
 , _age(p._age)
 {}
 
#else
 
 Person(Person&& p)
 : _name(move(p._name))
 , _sex(move(p._sex))
 , _age(p._age)
 {}
 
#endif
 
private:
 String _name;
 String _sex;
 int _age;
};
Person GetTempPerson()
{
 Person p("prety", "male", 18);
 return p; }
int main()
{ Person p(GetTempPerson());
 return 0; }
move 更多的是用在声明周期即将结束的对象上 为了保证移动语义的传递,程序员在编写移动构造函数时,最好使用 std::move 转移拥有资源的成员 为右值。
 
 
完美转发
 
完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数
 
void Func(int x) {
 // ......
}
template<typename T>
void PerfectForward(T t) {
 Fun(t);
}
PerfectForward 为转发的模板函数, Func 为实际目标函数。 完美转发是目标函 数总希望将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销。
所谓完美: 函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相 应实参是右值,它就应该被转发为右值 。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进 行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动语义)。
 
 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值