右值引用和移动语义

左值和右值

左值位于等号左边,可以对其取地址

右值位于等号右边(临时变量),不可以对其取地址

左值引用

指向左值的引用,我们经常使用的。左值引用无法指向右值,左值引用的本质是指针常量,但是不能对右值取地址。

note:const左值引用不会修改指向值,因此可以指向右值,。一般来说,临时变量在表达式结束之后就会被销毁,但是如果这个临时变量用来初始化一个const左值引用,这个临时变量的生命周期就会被延长,直到引用被销毁。

void push_back(const value_tyoe& val);

如果没有const,vec.push_back(1)这样的代码就无法编译通过。

右值引用

c++11引用了右值引用和移动语义,可以避免无谓的复制,提高了程序性能。

右值引用的标志是&&,可以指向右值,不能指向左值。右值引用的作用是可以修改右值。

std::move

功能是把左值强制转化成右值,让右值引用可以指向左值。等同于类型转化static_cast<T&&>(lvalue).右值引用能指向右值,本质上把右值提升为左值,使用std::move()转换该左值。

“`c++
int &&ref_a = 5;

等同于:
int tmp = 5;
int &&ref_a = std::move(tmp);

<pre><code class="line-numbers">**使用场景**

当类中含有指针指向堆空间时,我们就要在类中设置拷贝构造函数,避免类对象在进行复制时,不同对象的成员指向相同的对空间,造成内存多次释放。但是,当函数返回值为该类时,函数调用时会产生临时变量,该临时变量在拷贝构造完成后就销毁了,如果堆内存很大,那么,这个拷贝构造的代价会很大,带来了额外的性能损耗。我们可以使用移动构造函数来避免临时对象的拷贝构造。

“`cpp
class MyString{
private:
char* m_data;
size_t m_len;
void copyData(const char* data) {
m_data = new char[m_len+1];
memcpy(m_data, s, m_len);
m_data[m_len] = ‘\0’;
}
public:
MyString() {
m_data = NULL;
m_len = 0;
}
MyString(const char* p) {
m_len = strlen(p);
copyData(p);
}
MyString(const MyString& str) {
m_len = str.m_len;
copyData(str.m_data);
std::cout << “Copy Constructor is called! source: ” << str.m_data <<
std::endl;
}
MyString& operator=(const MyString& str) {
if(this != &str) {
m_len = str.m_len;
copyData(str.m_data);
}
std::cout << “Copy Assignment is called! source: ” << str.m_data <<
std::endl;
return *this;
}

MyString(MyString&& str) {
m_len = str.m_len;
m_data = str.m_data;
//move的意义
str.m_len = 0;
str.m_data = NULL;
std::cout << “Move Constructor is called! source: ” << str.m_data <<
std::endl;
}
MyString& operator=(MyString&& str) {
if(this != &str) {
m_len = str.m_len;
m_data = str.m_data;
str.m_len = 0;
str.m_data = NULL;
}
std::cout << “Move Assignment is called! source: ” << str.m_data <<
std::endl;
return *this;
}

~MyString() {
//有了移动构造函数,if判断很重要
if(m_data) delete []m_data;
}
};

int main() {
MyString str(“Hello”);
Mystring str2 = std::move(str); //Move Constructor is called! source: Hello;
Mystring str3 = Mystring(“Hello”); //Move Constructor is called! source: Hello;
str2 = Mystring(“Hello”); ////Move Assignment is called! source: Hello;
return 0;
}

完美转发

在函数模板中,可以将参数完美的转发给其它函数。不仅能准确的转发参数的值,还能保证被转发参数的左右值属性不变。
C++11的策略:

  1. 如果模板中函数的参数书写成T&&,那么,函数既可以接受左值引用,又可以接受右值引用。也就是说,左值引用和右值引用都可以调用该参数。
  2. 提供了模板函数std::forward(参数),用于转发参数,如果参数是一个右值,转发之后仍是右值引用;如果参数是一个左值,转发之后仍是左值引用。

STL中的emplace_back

push_back函数需要创建临时对象,而emplace_back不需要创建临时对象。

.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top