1、左值:可寻址的、引用的是某一个对象、可位于赋值运算符的左边、可以被赋值。
2、右值:可读的、引用的是某一个对象指向的地址中的数据、通常位于赋值运算符的右边、取出值赋给其他变量。
3、右值引用(T &&)
右值引用就是必须绑定到右值的引用,通过&&来获得右值引用;
声明一个右值引用:int &&a = 0
说明:
a、右值引用所引用的临时对象可以在该临时对象被初始化之后再次修改(这是为了允许move语义),即在右值引用被初始化后,可以使用左值或者左值引用改变右值引用所引用的临时对象的值。
b、能使用右值引用初始化给左值引用(即左值引用绑定到右值引用指向的对象),不能用左值或者左值引用初始化右值引用。
c、C++11使用move函数,用来获取绑定到左值或者左值引用上的右值引用。(需包含头文件utility)。
d、右值引用可以防止发生不必要的拷贝。(C++03中深拷贝会隐式的发生在值传递时期)
int a = 1;
//int &a = 1; //error, const int &a = 1 ok
//左值,左值引用,右值引用,初始化,赋值
int &aa = a;
//int &&aaa = a;//error,不能使用左值初始化右值引用
//int &&aaa = aa;//error,不能使用左值引用初始化右值引用
int &&aaa1 = std::move(a);//ok
int &&aaa2 = std::move(aa);//ok
std::cout << "a:" << a << std::endl; // 输出:a:1
std::cout << "&aaa1:" << &aaa1 << std::endl;// 输出:&aaa1:00D3DAE8
std::cout << "&aaa2:" << &aaa2 << std::endl;// 输出:&aaa2:00D3DAE8
int &&aaa = 0;
aaa = a;//ok, 可以使用左值改变右值引用指向对象的值。
aaa = aa;//ok, 可以使用左值引用改变右值引用指向对象的值。
int &&bbb = 1;
int b = bbb;//ok, 可以使用右值引用初始化左值
int &bb = bbb;//ok, 可以使用右值引用初始化左值引用
4、移动构造函数和移动赋值函数
移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的参数是一个右值引用。这意味着,移动构造函数的参数是一个右值或者将亡值的引用。也就是说,只有用一个右值,或者将亡值初始化另一个对象的时候才会调用移动构造函数。而move语句,就是将一个左值变成一个将亡值。
移动赋值函数的参数和普通赋值函数不同,移动赋值函数的参数是一个右值引用。
说明:
拷贝构造函数的调用时机:形实结合、函数返回值、使用一个对象出初始化另一个对象。
移动构造函数的调用时机:使用右值或者将亡值(对左值或者左值引用使用move函数取得将亡值)初始化另一个对象。
移动赋值函数的调用时机:使用右值或者将亡值赋值给另一个对象。
移动构造函数通常是noexcept。
#include "stdafx.h"
#include <iostream>
#include <utility>
#include <vector>
#include <cstring>
#include <cstdlib>
class CString{
public:
//普通构造函数
CString(char value[])
{
std::cout << "...普通构造函数..." << std::endl;
str = NULL;
int len = strlen(value);
str = (char *)malloc(len + 1);
memset(str, 0, len + 1);
strcpy(str, value);
}
//拷贝构造函数
CString(const CString &rhs)
{
std::cout << "...拷贝构造函数..." << std::endl;
str = NULL;
int len = strlen(rhs.str);
str = (char *)malloc(len + 1);
memset(str, 0, len + 1);
strcpy(str, rhs.str);
}
//移动构造函数
CString(CString &&rhs)
{
std::cout << "...移动构造函数..." << std::endl;
str = NULL;
str = rhs.str;
rhs.str = NULL;
}
//移动赋值函数
const CString& operator=(Str &&s)
{
std::cout << "移动赋值函数..." << std::endl;
if (str != NULL) {
free(str);
str = NULL;
}
str = s.str;
s.str = NULL;
return *this;
}
//析构函数
~CString()
{
std::cout << "...析构函数..." << std::endl;
if (str != NULL)
{
free(str);
str = NULL;
}
}
private:
char *str;
};
int main()
{
char strValue[] = "test";
CString str(value);// 调用普通构造函数
CString s1(strValue);
CString s2; //调用默认构造函数
s2 = std::move(s1);//调用移动赋值函数
std::vector<CString> vecStr1;
vecStr1.push_back(str);//调用拷贝构造函数
std::cout << vecStr1[0].str << std::endl;
if (str.str != NULL){
std::cout << str.str << std::endl;//运行会打印
}
std::vector<CString> vecStr2;
vecStr2.push_back(std::move(str));//移动构造函数(传递一个右值引用)
std::cout << vecStr2[0].str << std::endl;
if (str.str != NULL){
std::cout << str.str << std::endl;//运行不会打印
}
}
5、移动迭代器
C++11中可以使用make_move_iterator函数将一个普通的迭代器转换成移动迭代器(可避免拷贝操作提升性能)。