Modern C++ - Move & 移動構造/C++移動語義

557天前 · 程式設計 · c++ · 1754次阅读

Modern C++ - Move

移動語義是C++爲了提高資源管理效率而在C++11新增的特性。
它透過右值引用(T&&)、move移動構造等方法實現。

移動⌋並不真的轉移資源,而是轉移了資源的所有權生命週期

爲什麼需要移動語義

拷貝原對象新對象同時存在且相互獨立
這造成了大量的資源浪費性能開銷

移動語義的引入就是爲了解決該問題的。

移動語義如何避免拷貝開銷

移動的本質是轉移所有權生命週期,所以它實際上等同於這樣:

int main(){
    T t1;
    T t2;
    t2.data = t1.data; //手動將指標的所有權轉移
    t1.data = nullptr; //將原對象置空,表示沒有了原始數據的所有權
    //... 如果還有其他成員一樣的操作
}

即便沒有移動語義,也能夠做到類似的行爲。
但C++將其加入到了語言標準,使其更爲直觀規範並成爲了語言特性的一部分。

移動語義不僅方便且直觀,還更能表達出要轉移資源的意思:

int main(){
    T t1;
    T t2 = std::move(t1);
}

值型別

C++中,有左值右值兩種值型別
值型別可以用於區分是否進行移動操作
左值(left value),指向某個記憶體位址的具名對象,可以被取位址。
右值(right value),通常表示臨時對象和字面量,沒有具名的內存位址,無法被取位址。

10; //這就是一個字面量右值,無具體名稱。
int x; // x 就是一個左值,它有一個變數名 x,可以被取位址

理解值類別對於掌握移動語義非常重要。

tips: 值類別值型別是兩種概念,且它們同時存在

int; //這是值類型
10; //這是一個值型別爲右值 類型爲int的對象
int x = 10; //類型爲int的右值,被賦值到了類型爲int的變數具名 x。

移動語義的實現

爲了實現移動語義,C++11中新增了類型 右值引用(T&&),與之相對的便是左值引用(T&)。

std::move實際上做的是將左值轉換爲右值,具體來說實現可能類似這樣:

template<T>
T&& std::move(T&& val) noexcept {
    return static_cast<T&&>(val);
}

同時,要爲類支援移動構造只需要重載一個接收右值的構造函式即可:

struct Test{
    int * data;
    Test(){}; 
    ~Test(){ delete data;};
    Test(Test && other) noexcept {//移動構造函式,你需要在這裏實現對移動的支援
        data = other.data;
        other.data = nullptr; //置空原對象,表示原對象沒有了資源的所有權與避免重複釋放
    }
};

int main(){
    Test t1;
    Test t2 = std::move(t1);
}

移動也伴隨着生命週期的轉移

int main(){
    Test t1;
    { //一個作用域
        Test t2; // t2.data記憶體位址0x01,資源的生命週期到該作用域(花括號)結束
        t1 = std::move(t2); // 移動後,0x01的生命週期被綁定到了t1
    }
} // t1生命週期結束,此時0x01才被釋放

可以參閱RAII的說明。

移動後,原對象的狀態爲未定義(Undefined behavior)但可解構,其不應再被使用 (即便它可能有效)。
未定義行爲表示任何可能,程式的行爲不受任何保證。
它可能可以按照你的預期運行,可能是一個無用值,或是引發程序崩潰。
不少初學者難以理解的問題,都是因爲使用到了未定義行爲

引用摺疊、拷貝消除和完美轉發

移動語義增加了右值引用,並由此衍生出了一系列的特性與方法。
引用摺疊拷貝消除完美轉發是其中之一。

引用摺疊

引用摺疊用於在模板編程,引用類型的組合會被 ⌈摺疊⌋成對應的類型。
它的規則大致如下:

  • T& &T& &&T&& & 會摺疊爲 T&
  • T&& &&會摺疊爲 T&&

這意味著:一個左值引用和任何引用組合結果都是左值引用,而右值引用和右值引用組合結果依然是右值引用。

範例:

template<T>
void func(T&& val){ //val的類型取決與傳入的是左值還是右值
}

int main(){
    int x = 10;
    func(x); // 這裏 T 是 int& (左值),val類型展開是int& && 並摺疊爲 int&(左值) 類型。
    func(10); //這裏 T 是 int&& (右值),val類型展開爲int&& &&,然後摺疊爲int &&(右值) 類型。
}

在這個例子中,如果傳入左值,T會被推斷為int&,而如果傳入右值,T就是int。

拷貝消除

拷貝消除是C++標準規定的編譯器優化技術,用來減少不必要的對象拷貝。
其適用與以下兩種場景:

  • 返回值優化(Return Value Optimization, RVO):當函式傳回一個臨時對象時,拷貝消除允許編譯器直接在返回對象的內存位置構造對象。從而避免多餘的拷貝。
  • NRVO(Named Return Value Optimization):NRVO是RVO的一種特殊情況,當函式返回具名對象時,編譯器仍可消除拷貝。
template<T>
T make(){
    return T(); // RVO: 編譯器會直接在 main 函數中構造 T() 的對象
}

template<T>
T make2(){
    T res;  // NRVO: 編譯器會優化 res,直接在 main 函數中構造該對象
    return res;
}
int main(){
    int x = make<int>(); // 使用 RVO
    int x2 = make2<int>(); // 使用 NRVO
}

RVONRVO的區別在於是否返回具名對象

值得注意的是,當函式返回的對象生命週期不超過函式結束才能觸發NRVO

T func(){
    T res; // 這是一個⌈臨時對象⌋,因爲其生命週期等於函式結束
    return res; // 可以觸發NRVO
}
T t1;

T func2(){
    return t1; // 無法觸發NRVO,因爲返回的對象生命週期超過函式結束
}

T func3(){
    static T t2;
    return t2; // 這裏也一樣,無法觸發NRVO
}

完美轉發

引用摺疊是 完美轉發(Perfect Forwarding) 的基礎。
完美轉發允許模板函數不改變參數的值型別,直接轉發給其他函數 (避免參數在多個函式中傳遞引用規則變更值型別)。
這是透過右值引用std::forward實現的。

template<T>
void func1(T && val){
}

template<T>
void func2(T&& val){
    //...進行一些操作,然後需要調用func1
    func1(std::forward<T>(val)); // 使用完美轉發避免值型別發生變更
}

小結

移動語義是C++爲了提高資源管理效率新增的特性。
它的實現依賴右值引用(T&&)、move移動構造,使資源的所有權可以被有效轉移。
這帶來了以下幾個重要的相關特性:

  • 值型別用於區分是否進行移動操作
  • 引用摺疊用於自動組合並區分在模板編程中的值類別
  • 拷貝消除是標準化的移動語義實現優化,進一步提高性能,
  • 完美轉發用於參數在多個函式間傳遞時,避免因傳遞過程變更值類別

參閱:

  1. https://en.cppreference.com/w/cpp/language/move_constructor
  2. https://en.cppreference.com/w/cpp/language/copy_elision
  3. https://en.cppreference.com/w/cpp/language/value_category
  4. https://en.cppreference.com/w/cpp/language/reference
  5. https://en.cppreference.com/w/cpp/utility/forward
👍 2

modern c++ c++ move nrvo

最后修改于94天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡

狗头

  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头

原神

  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神

小黄脸

  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  1. gvuzjgskfy 460天前

    选材新颖独特,通过细节描写赋予主题鲜活生命力。

目录

avatar

Hina

曇花一現

20

文章

96

评论

8

分类

初见

Oha Api隨機背景圖像Api-隨機音樂&在線免費聆聽jaychou音樂!

523天前

OwO

33

網站正在更新中...
站點正在更新功能與樣式,如有樣式錯誤,請嘗試刷新緩存