C++里面除了普通的左值引用(string&)以外,还有右值引用(如string&&),以及引用折叠。右值引用主要是为了开发人员作为一个C++库的开发者,可以配合编译器更有效地优化代码而发明,我们来看下面这么一个例子:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<int> MakeVector(int startP, int endP) {
vector<int> numbers;
for (int i = startP; i <= endP; i++) {
numbers.push_back(i);
}
return numbers;
}
class Sum {
private:
vector<int> numbers;
public:
explicit Sum(const vector<int>& theNumbers):numbers{theNumbers}
{}
int Get() {
int sum = 0;
for (auto &i : numbers) {
sum += i;
}
return sum;
}
};
int main() {
Sum sum{ MakeVector(3, 7) };
cout << sum.Get() << endl;
system("PAUSE");
return 0;
通过visual studio的断点操作我们可以发现,这里会三次调用vector构造函数操作:
Vector::vector():发生在MakeVector里面构造numbers的时候
Vector::vector(vector&&):发生在makevector里面return的时候
Vector::vector(const vector<int>& theNumbers):numbers{theNumbers}
这第二次调用return的时候就是右值引用
右值引用其实说的就是已经再也不需要被用到的对象的引用,譬如makeVector里面,
Return numbers里面的numbers.在return了之后,这个变量已经没有用了,所以会使用右值引用复制构造函数
那这个构造函数有什么不同之处呢?我们可以想象里面维护了一个指向数组的指针,而复制vector实际上就是要构造一个新的数组,然后把复制的值都拿过去,但我们知道正在被复制的vector已经在别的地方用不到了,那我们可以把它的指针直接拿过来用
我们只需把指针传过去后,自己这边设置为空指针即可
右值引用是十分有好处的,可以节省很多复制对象的开销,调用右值引用的复制构造函数,我们可以认为对象直接跑过去了,并没有发生任何复制
我们来看之前string例子中的一些操作:
String& operator=(String&& theString)
{
if (this != &theString)
{
delete[] buffer;
length = theString.length;
buffer = theString.buffer;
theString.buffer = nullptr;
}
return *this;
}
String(String&& theString):length{theString.length},buffer{ theString.buffer}
{
theString.buffer = nullptr;
}
这是一个复制构造函数和赋值号的重载操作,使用了右值引用传递法,这时候和链表一样,只需把指针覆过去并进行空指针操作即可
不需要像左值引用一样调用相关接口进行操作
折叠:
#include <iostream>
using namespace std;
void Print(int& x)
{
cout << "x is int&" << endl;
}
void Print(int&& x)
{
cout << "x is int&&" << endl;
}
template<typename T>
void CallPrint(T&& x)
{
Print(x);
}
int main()
{
int x = 0;
CallPrint(x);
CallPrint(0);
return 0;
}
因为CallPrint(0)里面的0是右值,而callPrint的参数x类型是T&&,那么T只能是int->参数X的类型是int&&。但是尽管如此,这里的参数x已经有一个名字了C++语法并不阻止我们在print(X)后继续使用x,所以是左值引用