1.左值右值的区别
左值:可以取其地址
右值:不能取其地址
int b=1;//b为左值
int*p=new int(0);//p为左值
const int c=10;//c为左值
//右值
int x=10;int y=3;
10;
x+y;//表达式计算的值
fmax(x,y);//函数返回值
2.左值引用与右值引用
左值引用:
- 左值引用只能引用左值,不能引用右值
- const修饰左值引用可以引用左值也可以引用右值
int a = 10;
int& b = a;//左值引用引用左值
const int& c = 10;//const修饰的左值引用右值
右值引用:
- 右值引用只能引用右值或者move后的左值。
int&& a = 10;//右值引用引用常量
int num = 4;
int&& b = std::move(num);//右值引用只能引用move后的左值
3.右值引用的使用场景
左值引用用来做参数或返回值,减少拷贝提高效率。
右值引用:
针对深拷贝的类,使用右值引用增添移动构造和移动赋值提高效率
下面的代码是简单实现了C++string库以及整数转换为字符串to_string函数实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace NUC
{
class string
{
public:
string(const char* str = "")
:size(strlen(str))
, capacity(size)
{
cout << "构造函数" << endl;
_src = new char[capacity + 1];
strcpy(_src, str);
}
~string()
{
cout << "析构函数" << endl;
delete[] _src;
_src = nullptr;
}
void swap(string &s)
{
::swap(_src, s._src);//全局swap std::swap
::swap(size, s.size);
::swap(capacity, s.capacity);
}
string(const string& s)
:_src(nullptr)
{
cout << "拷贝构造函数" << endl;
string tmp(s._src);
swap(tmp);
}
string& operator=(const string& s)
{
cout << "operator=" << endl;
string tmp(s);
swap(tmp);
return *this;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
char* begin() { return _src; }
char* end() { return _src + size; }
void reserve(size_t i)
{
if (i > capacity)
{
char* tmp = new char[i + 1];
strncpy(tmp, _src, size);
delete[]_src;
_src = tmp;
capacity = i;
}
}
void push_back(char ch)
{
if (size == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
reserve(newcapacity * 2);
}
_src[size] = ch;
_src[size + 1] = '\0';
size++;
}
private:
char*_src;
int size;
int capacity;
};
NUC::string to_string(int value)
{
bool flag = true;
if (value < 0)
{
flag = false;
value = 0 - value;
}
NUC::string str;
while (value > 0)
{
int x = value % 10;
value /= 10;
str += ('0' + x);
}
if (flag == false)
{
str += '-';
}
std::reverse(str.begin(), str.end());
return str;
}
}
#include"My_string.h"
void TestString()
{
/*NUC::string str("abc");
str += 'd';*/
NUC::string str2 = NUC::to_string(-123);
}
int main()
{
TestString();
return 0;
}
此时调用to_string时返回str时,编译器会处理为右值,此时调用拷贝构造函数,因为拷贝构造函数参数为const string& s
可以引用右值
移动构造函数
C++11为了提高效率,提供了移动构造函数,移动资源
在类中添加
// 移动构造
string(string&& s)
:_src(nullptr), size(0), capacity(0)
{
cout << "string(string&& s) -- 移动构造" << endl;
this->swap(s);
}
此时调用to_string时返回str时,编译器会处理为右值,此时调用移动构造函数转移资源。
两次析构函数分别析构了函数to_string里面的str以及主函数的str2对象
注意:编译器优化情况与编译器有关
移动语义(move)
移动赋值运算符重载
使用场景:
#include"My_string.h"
void TestString()
{
NUC::string str1;
str1 = NUC::to_string(-123);
}
int main()
{
TestString();
return 0;
}
如果没有移动赋值运算符重载时,因为str1在调用函数前已经初始化好,此时NUC::to_string(-123)
会调用string& operator=(const string& s)
运算符重载。
在string类中添加移动赋值
// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
this->swap(s);
return *this;
}
对比减少了临时对象生成,减少了拷贝提高了效率。