目录
左右值引用概念
右值引用的作用
左右值引用概念
什么是左值?什么是左值引用?
左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
什么是右值?什么是右值引用?
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址。右值引用就是对右值的引用,给右值取别名。
所以我们判断一个值是左值还是右值我们可以用取地址来判断,能取地址的就是左值反之就是右值
a为左值,a+b就是典型的右值。
对于左值引用和右值引用的语法规则很简单,一个&就是引用左值,两个&&就是右值。
void test1()
{
int a = 10;
int b = 20;
int& sa = a;//左值引用
int&& sb = a + b;右值引用
}
他们之间可以互相转换。
void test1()
{
int a = 10;
int b = 20;
//我们知道右值是一个常量,不能被修改所以可以用const修饰,再来接收
const int& sa = a + b;
//左值引用给右值引用可以用move进行转换。当一个值move一下就变成了右值。
int&& sb= move(a);
}
那右值究竟可以用来干什么呢?
右值引用的作用
首先我们要知道右值是一个将亡值,一般右值都是一个临时的值。比如上面的a+b就是一个将亡值
只是被右值引用后会存到特定的空间取去。出来作用域也是会自动销毁的。
所以我们可以用这个来实现移动构造,只需要交换将亡值里的内容,这样做能大大减少内部的重复拷贝
假如我们有了一个我们自己实现的string
namespace bit
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
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);
}
// s1.swap(s2)
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 拷贝构造用swap交换资源达到减少拷贝的目的
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 test1()
{
bit::string s2 = bit::string("aaa");
}
假如我们没写移动构造
但是我们写了移动构造
中间其实就少了一层构造。但是当我们执行代码的时候并没有去打印我们的深拷贝。
这是因为编译器有优化,这里编译器会认为中间的的构造完全没必要所以只需要一个默认构造就行了。
当我们加上默认构造打印语句后
我们这时候只需要将我们的临时变量move一下它就会去匹配右值版本了.
非常的好用.