1、What
C++11引入的一种新型引用,用于提高程序的性能和资源管理的效率。它通过&&
符号来表示,只能绑定到右值(包括将亡值,即临时对象)上,不能绑定到左值上。
1.1 左值和右值的区别
左值是一个表示数据的具名表达式,可以获取它的地址并且对其赋值,可出现在赋值操作符=
的左边,具有一段较长的生命周期。右值是一个表达数据的匿名表达式,比如字面常量、表达式返回值、函数返回值等,其生命周期很短,可出现在赋值操作符=
的右边,但是不能出现在=
的左边,右值不能取地址。
1、整型x的x++是右值,编译器首先生成x值的临时复制,然后对x递增,最后返回临时复制内容。++x则是左值,对x递增后马上返回其自身。
2、通常字面量是右值,但字符串字面量除外。
1.2 补充概念:泛左值、将亡值、纯右值
泛左值:通过评估能够确定对象、位域或函数的标识的表达式。
纯右值:通过评估能够用于初始化对象和位域,或者能够计算运算符操作数的值的表达式。
将亡值:属于泛左值的一种,表示资源可以被重用的对象和位域,它们接近其生命周期的末尾;也可能经过右值引用的转换产生。
2、Why
为了解决C++中资源管理的效率和性能问题。对象的复制和移动是常见的操作,当对象包含大量数据时,复制操作会导致性能开销。右值引用允许转移对象的资源(如动态分配的内存、文件句柄等),而不是通过复制来共享资源,可以显著减少不必要的拷贝和内存分配,提高程序的运行效率。
3、When
移动语义:将资源(如动态内存、文件句柄等)从一个对象“移动”到另一个对象,而不是复制它们。如在将大型对象(如
std::vector
、std::string
等)赋值给另一个对象时,使用右值引用可以显著提高性能。完美转发:在模板编程中保持参数的原始类型(左值或右值),以便在转发给另一个函数时保持其行为(例如,是应该进行拷贝还是移动)。这可以避免在函数调用过程中丢失参数类型信息的问题,从而提高代码的可维护性和灵活性。
4、How
1、声明右值引用
在类型名称后面加上&&符号。如:
int&& rref = 10; // rref 是一个右值引用,绑定到整数 10 这个右值上
2、作为函数参数
如果函数的参数是右值引用,也需要在类型名称后面加上&&符号。如:
void foo(int&& x) { // 函数体内可以使用 x,它是对传入右值的引用
// 函数体
}
3、实现移动构造函数和移动赋值运算符
定义移动构造函数和移动赋值运算符,允许资源从源对象直接“移动”到目标对象,而不是通过复制来共享资源。如:
class String {
public:
String(const char* str = "") : _str(new char[strlen(str) + 1]) {
strcpy(_str, str);
}
// 移动构造函数
String(String&& other) noexcept : _str(other._str) {
other._str = nullptr; // 将源对象的资源转移给目标对象,并清空源对象
}
// 移动赋值运算符
String& operator=(String&& other) noexcept {
if (this != &other) {
delete[] _str;
_str = other._str;
other._str = nullptr;
}
return *this;
}
~String() {
delete[] _str;
}
// 禁止拷贝构造和拷贝赋值
String(const String&) = delete;
String& operator=(const String&) = delete;
void print() const {
std::cout << _str << std::endl;
}
private:
char* _str;
};
4、使用std::move
std::move是一个函数模板,它的主要作用是将一个对象转换为右值引用,以便可以对其进行移动操作。但需要注意的是,std::move本身并不移动任何东西,它只是将对象转换为右值引用,让编译器知道该对象可以被移动。如:
String s1("Hello");
String s2 = std::move(s1); // 使用 std::move 将 s1 的资源移动给 s2
在这个例子中,我们使用std::move将s1转换为右值引用,并将其资源移动给s2。
5、使用std::forward(完美转发)
std::forward
是一个函数模板,其保持参数的引用性质(左值引用或右值引用)。
通用引用(也称为转发引用)T&&
,可以绑定到左值或右值。
#include <iostream>
#include <utility>
class Wrapper {
public:
Wrapper(int& value) : value_(value) {
std::cout << "Constructed with lvalue reference\n";
}
Wrapper(int&& value) : value_(value) {
std::cout << "Constructed with rvalue reference\n";
}
~Wrapper() {
// Do nothing in this simple example
}
void print() const {
std::cout << "Value: " << value_ << std::endl;
}
private:
int& value_;
};
template<typename T>
void forwardWrapper(T&& t) {
Wrapper w(std::forward<T>(t));
w.print();
}
int main() {
int x = 42;
// 传递左值
forwardWrapper(x);
// 传递右值
forwardWrapper(42);
return 0;
}