为了提高程序运行效率,C++11中引入了右值引用,右值引用也是别名,但其只能对右值引用
int main()
{
//右值引用---->引用形式----->只能引用右值
int a = 10;
int b = 20;
//a可以为左值,能放到=号左侧的一定是左值
//能放到=号右侧的不一定是右值
a = b;
//ra对10的右值引用,ra称为对10的别名
int&& ra = 10;
system("pause");
return 0;
}
一般认为认为:可以放在=左边的,或者能够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但肯定有特殊情况,不一定上述说法就完全正确
int main()
{
const int a = 10;
//a = 100; a不能出现在左侧
//a也不是右值
//a能够取地址
cout << &a << endl;
//int && ra = a;
system("pause");
return 0;
}
左值与右值不是很好区分,一般认为:
int b = 10;//为右值
int&& rb = b + 10;
int g_a = 10;
int& GetG_A()
{
return g_a;
}
int main()
{
GetG_A() = 10;
//下面这行代码编译报错
//int && rc = GetG_A();
return 0;
}
不能简单的通过能否放在=左侧右侧或者取地址来判断左值或者右值,要根据表达式结果或变量的性质判断,比如上述:c常量
右值引用,顾名思义就是对右值的引用。C++11中,右值由两个概念组成:纯右值和将亡值。
//将亡值
//值得方式返回
//使用ret--->创建一个临时变量---->函数运行结束栈空间被回收
int Add(int a, int b)
{
int ret = a + b;
return ret;
}
int main()
{
int&& Ret = Add(10,20);
return 0;
}
//C++98引用
//1. 普通类型得引用
//2. cosnt类型的引用
int main()
{
//98中的普通类型的引用不能引用右值
int a = 10;
int&ra = a;
//int&rra = 10; 不行
//98中的const引用既可以引用左值也可以引用右值
const int&cral = a;
const int& cra2 = 10;
return 0;
}
C++11中右值引用:只能引用右值,一般情况不能直接引用左值
int main()
{
//10纯右值,本来只是一个符号,没有具体空间
//右值引用变量r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量
int&& r1=10;
r1=100;
int a=10;
int&& r2=a; //编译失败:右值引用不能引用左值
return 0;
}
C++11中,std::move()函数位于 头文件中,这个函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,通过右值引用使用该值,实现移动语义。 注意:被转化的左值,其声明周期并没有随着左右值的转化而改变,即std::move转化的左值变量不会被销毁。
int main()
{
int a = 10;
int&& ra = move(a);
return 0;
}
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 operator+(const String&s)
{
char* pTemp = new char[strlen(_str) + strlen(s._str)];
strcpy(pTemp, _str);
strcpy(pTemp + strlen(_str), s._str);
String strRet(pTemp);
delete pTemp;
return strRet;
}
~String()
{
if (_str) delete[] _str;
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2("world");
String s3(s1 + s2);
return 0;
}
假设编译器在返回值位置不优化
上述代码在加号运算符的重载时,是创建了临时变量,存储拼接起来的字符串,返回的时候是按值返回是通过strRet拷贝了一个临时的对象。函数体内的strRet在return后,已经被销毁。所以最后s3拷贝构造的时候是通过临时对象构造的,所以s3也要再创建一个临时变量,一但拷贝构造好之后,临时对象就释放掉了
一个刚释放一个又申请,有点多此一举。能不能不让s3再创建一个临时对象
我们可以让s3直接使用返回值的临时对象。
C++11提出移动语义概念:将一个对象中资源移动到另外一个对象的方式
//移动构造
//移动构造参数一定不能加cosnt,资源可以转移出去,但是资源不能清空了
String(String&& s)
:_str(s._str)
{
s._str = nullptr;
}
auto_ptr<int> sp1(new int);
auto_ptr<int> sp2(sp1);
//sp1资源给了sp2,但是sp1生命周期还没有到头,所以不能被使用
通过move函数把左值转化为右值。
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)
{}
//移动赋值
Person& operator=(Person&&p)
{
return *this;
}
#endif
private:
String _name;
String _sex;
int _age;
};
Person GetTempPerson()
{
Person pp("prety", "male", 18);
return pp; //pp确实是右值,但是里面的成员变量是当作左值,所以用move把左值转化为右值
}
int main()
{
Person p(GetTempPerson());
system("pause");
return 0;
}
用move的时机,确定当前变量是将亡值,例如函数返回值。
完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数
void Func(int x)
{
// ......
}
template<typename T>
void PerfectForward(T t)
{
Fun(t);
}
PerfectForward为转发的模板函数,Func为实际目标函数,但是上述转发还不算完美**,完美转发是目标函数总希望将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销**,上述代码中的Fun(t)只是外部t的一份拷贝,就好像转发者不存在一样。
函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相应实参是右值,它就应该被转发为右值。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动语义)。
C++11 通过forward函数来实现完美转发:
void Fun(int &x) //普通类型引用
{
cout << "lvalue ref" << endl;
}
void Fun(int &&x) //普通类型右值引用
{
cout << "rvalue ref" << endl;
}
void Fun(const int &x) //const类型普通引用
{
cout << "const lvalue ref" << endl;
}
void Fun(const int &&x) //const类型的右值引用
{
cout << "const rvalue ref" << endl;
}
//通过函数模板作为转发函数
template<typename T>
//完美转发:t是左值---->传给Fun函数,t应该也是左值
// t如果是右值--->传给Fun函数,t应该是右值
void PerfectForward(T &&t)
{
Fun(std::forward<T>(t)); //forward把t转化为右值。
//Fun(t);
}
int main()
{
PerfectForward(10); // 10为右值
int a;
PerfectForward(a); // lvalue ref
PerfectForward(std::move(a)); // rvalue ref
const int b = 8;
PerfectForward(b); // const lvalue ref
PerfectForward(std::move(b)); // const rvalue ref
return 0;
}
emplace
插入(尾插)也用到了右值引用。pash_back;必须要把对象构造好
emplace:构造加尾插