转移语义的定义:
右值引用是用来支持转移语义的。转移语义可以将资源从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。(注意:不是复制不是粘贴)
作用和剪切粘贴一样的,通过转移语义,临时对象的值能够转移到其他对象里。
当然要实现功能,就要定义相应的构造函数,我们先定义一个类。
class MyString
{
private:
char* _data;
size_t _len;
void _init_data( const char* s);
public:
MyString( const MyString& str);
MyString&operator=( const MyString& str);
virtual ~MyString();
};
void
MyString::_init_data(const char *s)
{
_data = new char[_len+1];
memcpy( _data, s, _len);
_data[_len] = '\0';
}
MyString::MyString( const MyString& str)
{
this->_len = str._len;
_init_data( str._data );
std::cout << "Copy Constructor is called! source: " << str._data << std::endl;
return *this;
}
MyString& operator=(const MyString& str)
{
if(this != &str)
{
_len = str.len;
_init_data(str._data);
}
std::cout << "Copy Assigment is called! source: " << str._data << std::endl;
return *this;
}
virtual MyString::~MyString()
{
if( this->_data)
{
free(_data);
}
}
若将参数做右值引用,则:
转移构造函数:
MyString(MyString&& str) {
_len = str._len;
_data = str._data;
str._len = 0;
str._data = NULL;
转移操作符:
MyString& operator=(MyString&& str)
{
if (this != &str) {
_len = str._len;
_data = str._data;
str._len = 0;
str._data = NULL;
}
return *this;
}
可见和拷贝构造函数类似,有几点需要注意:
* 参数(右值)的符号必须是右值引用符号,即“&&”。
* 参数(右值)不可以是常量,因为我们需要修改右值。
* 参数(右值)的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。
标准库函数std::move:
标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。
std::move使用栗子,下面是一个简单的swap函数模板:
template <class T> swap(T& a, T& b)
{
T tmp(a);
a = b;
b = tmp;
}
此函数使用一次,分别要对a,b,temp都进行一次拷贝工作,但如果使用std::move就可避免三次没必要的拷贝。
template <class T> swap(T& a, T& b)
{
T tmp(std::move(a));
a = (std::move(b));
b = (std::move(tmp));
}
还有精确传递:
精确传递适用于这样的场景:需要将一组参数原封不动的传递给另一个函数。原封不动指 左值/右值和 const/non-const 属性和参数值不能变。
所以一般定义函数时需要const和非const 两种类型重载,很明显这使程序变得低效,但直接使用右值引用传入参数,只需要定义一次,就能够将所有的参数类型原封不动的传递给目标函数。
例如:
原本需要重载函数:
template <typename T> void forward_value(const T& val)
{
process_value(val);
}
template <typename T> void forward_value(T& val)
{
process_value(val);
}
使用右值引用只需一个函数:
template <typename T> void forward_value(T&& val)
{
process_value(val);
}