左值/右值與應用

關於什麼是左值、什麼是右值,可以參考官方定義

https://en.cppreference.com/w/cpp/language/value_category

還有一篇文章

https://zhuanlan.zhihu.com/p/54050093

理解左值 右值

下面將對左值和右值的用法進行剖析,反向理解什麼是左值、右值。

右值據有兩個特點:

所引用的對象將要被銷燬

該對象沒有其他用戶

使用右值引用代碼可以自由地接管所引用的對象的資源

程序的數據一般放在內存,那麼需要用到的時候,直接取用。例如

int a = 10;

當我們需要使用a這個名字所代表的內存值,通過a就可以找到這塊內存。並且可以做如下操作

a = a + 1

在上面的式子中,將a的值取出到寄存器與1相加,再把相加後的值放回a中。同時,上式中a是左值,a+1是右值,可以看出,a+1的過程是在寄存器中進行的,結果也放在寄存器中。然後再把結果放回a指向的內存。下一刻,該寄存器的a+1可能又被什麼運算的結果覆蓋掉了。

這就是primer 5th所說的所引用的對象將要被銷燬(p471)。

爲了不浪費這個值(a+1),c++編譯器通過右值引用的方式延長a+1(這個值在寄存器中)。例如

int &&r = a + 1;
int v = r;
int &&r2 = std::move(r);

r是a+1的右值引用,同時r是左值。

基本數據類型有左右值之分,類也有左右值。即如果可以使用右值引用綁定基本類型,那麼也可以綁定類。對於類而言,存在移動構造函數和移動賦值函數。

什麼時候調用移動操作,類似上面的第二個賦值操作,此時就會調用移動操作,竊取寄存器資源。所以需要實現類的移動構造函數和移動賦值函數。

掌握移動操作

右值應用最強大的地方是在類能夠輕易使用右值,減少拷貝的開銷

下面是移動構造函數(primer 5th p473)

StrVec::StrVec(StrVec &&s) noexcept
    : elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    s.elements = s.first_free = s.cap = nullptr;
}

下面代碼是沒有意義的,只是爲了說明

StrVec sv1;                  //(1)

StrVec sv2 = sv1;            //(2)

StrVec sv3 = std::move(sv1); //(3)

這裏涉及到&&s這個變量,如果用一個右值去調用StrVec構造函數(3),不會重新分配任何內存,而是直接接管s的內存。最終s(移源後的對象 sv1)被銷燬了,則調用析構函數,而析構函數析構的elements/first_free/cap都被置爲nullptr,不會影響sv3。

但是這裏需要注意,因爲sv1使用了move,將sv1左值轉換爲右值使用,除了對sv1賦值和銷燬(離開作用域),不能使用sv1的值。即可以賦予它新值,但是不能使用移源後對象的值。

StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{
    if (this!= &rhs) {
        free();
        elements = rhs.elements;
        first_free = rhs.first_free;
        cap = rhs.cap;
        rhs.elements = rhs.first_free = cap = nullptr;
    }
    return *this;
}

移動構造和移動賦值都是將移源後的對象置於與默認初始化的對象相同的狀態。

沒有移動操作,對於右值對象的拷貝和賦值操作,都會調用最普通的拷貝構造和賦值操作。有了移動操作,主要有哪些應用場景呢?

1、函數返回的是一個右值,就可以調用移動操作

 

class HasPtr {
public:
    HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) {p.ps = 0;}
    HasPtr& operator=(HasPtr rhs)
            { swap(*this, rhs); return *this; }
}

在類代碼小心使用move,可以大幅度提升性能。但是一個移源後對象具有不確定的狀態,對其調用std::move是危險的。調用move時,必須絕對確認移源後對象沒有其他用戶

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章