一、 新特性的目的右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和精确传递 (Perfect Forwarding)。它的主要目的有两个方面:
1. 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。 二、 何为右值C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。 所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。 右值是指临时的对象,它们只在当前的语句中有效。
在C++11之前,右值是不能被引用的,如:
我们最多只能用常量引用来绑定一个右值,如:
在C++11中,我们可以引用右值,使用
三、 应用场景有如下string类,实现了拷贝构造函数和赋值运算符重载。 class MyString {
private:
char* _data;
size_t _len;
void _init_data(const char *s) {
_data = new char[_len + 1];
memcpy(_data, s, _len);
_data[_len] = '\0';
}
public:
MyString() {
_data = NULL;
_len = 0;
}
MyString(const char* p) {
_len = strlen(p);
_init_data(p);
}
MyString(const MyString& str) {
_len = str._len;
_init_data(str._data);
std::cout << "Copy Constructor is called! source: " << str._data << std::endl;
}
MyString& operator=(const MyString& str) {
if (this != &str) {
_len = str._len;
_init_data(str._data);
}
std::cout << "Copy Assignment is called! source: " << str._data << std::endl;
return *this;
}
virtual ~MyString() {
if (_data != NULL) {
std::cout << "Destructor is called! " << std::endl;
free(_data);
}
}
};
int main() {
MyString a;
a = MyString("Hello");
std::vector<MyString> vec;
vec.push_back(MyString("World"));
}
运行结果: Copy Assignment is called! source: Hello
Destructor is called!
Copy Constructor is called! source: World
Destructor is called!
Destructor is called!
Destructor is called!
总共执行了2次拷贝, 通过加入定义 MyString(MyString&& str) {
std::cout << "Move Constructor is called! source: " << str._data << std::endl;
_len = str._len;
_data = str._data;
str._len = 0;
str._data = NULL; // ! 防止在析构函数中将内存释放掉
}
MyString& operator=(MyString&& str) {
std::cout << "Move Assignment is called! source: " << str._data << std::endl;
if (this != &str) {
_len = str._len;
_data = str._data;
str._len = 0;
str._data = NULL; // ! 防止在析构函数中将内存释放掉
}
return *this;
}
运行结果: Move Assignment is called! source: Hello
Move Constructor is called! source: World
Destructor is called!
Destructor is called!
需要注意的是:右值引用并不能阻止编译器在临时对象使用完之后将其释放掉的事实,所以 四、 标准库函数 std::move既然编译器只对右值引用才能调用 void ProcessValue(int& i) {
std::cout << "LValue processed: " << i << std::endl;
}
void ProcessValue(int&& i) {
std::cout << "RValue processed: " << i << std::endl;
}
int main() {
int a = 0;
ProcessValue(a);
ProcessValue(std::move(a));
}
运行结果: LValue processed: 0 RValue processed: 0 std::move在提高 swap 函数的的性能上非常有帮助,一般来说,swap函数的通用定义如下: template <class T>
void swap(T& a, T& b)
{
T tmp(a); // copy a to tmp
a = b; // copy b to a
b = tmp; // copy tmp to b
}
有了std::move,再结合右值引用,就可以避免不必要的拷贝了。 swap函数的定义变为 : template <class T>
void swap(T& a, T& b)
{
T tmp(std::move(a)); // move a to tmp
a = std::move(b); // move b to a
b = std::move(tmp); // move tmp to b
}
可以使用第三节中的MyString类进行测试: int main() {
MyString a("a");
MyString b("b");
swap(a, b);
return 0;
}
五、 精确传递(Perfect Forwarding)精确传递就是在参数传递过程中,所有这些属性和参数值都不能改变。在泛型函数中,这样的需求非常普遍。 举例说明比较好理解。 forward_value函数只有一个参数val,定义如下: template <typename T>
void forward_value(const T& val) {
process_value(val);
}
template <typename T>
void forward_value(T& val) {
process_value(val);
}
函数 forward_value 为每一个参数必须重载两种类型,T& 和 const T&,否则,下面四种不同类型参数的调用中就不能同时满足: int a = 0;
const int &b = 1;
forward_value(a); // int&
forward_value(b); // const int&
forward_value(2); // int&
对于一个参数就要重载两次,也就是函数重载的次数和参数的个数是一个正比的关系。这个函数的定义次数对于程序员来说,是非常低效的。我们看看右值引用如何帮助我们解决这个问题: template <typename T>
void forward_value(T&& val) {
process_value(val);
}
只需要定义一次,接受一个右值引用的参数,就能够将所有的参数类型原封不动的传递给目标函数。 经测试,VS2015已经支持右值引用&&。
|
|