c++11的右值引用、移動語義

来源:http://www.cnblogs.com/adinosaur/archive/2016/08/26/5805235.html
-Advertisement-
Play Games

對於c++11來說移動語義是一個重要的概念,一直以來我對這個概念都似懂非懂。最近翻翻資料感覺突然開竅,因此記下。其實搞懂之後就會發現這個概念很簡單,並無什麼高深的地方。 先說說右值引用。右值一般指的是表示式中的臨時變數,在c++中臨時變數在表達式結束後就被銷毀了,之後程式就無法再引用這個變數了。但是 ...


對於c++11來說移動語義是一個重要的概念,一直以來我對這個概念都似懂非懂。最近翻翻資料感覺突然開竅,因此記下。其實搞懂之後就會發現這個概念很簡單,並無什麼高深的地方。

先說說右值引用。右值一般指的是表示式中的臨時變數,在c++中臨時變數在表達式結束後就被銷毀了,之後程式就無法再引用這個變數了。但是c++11提供了一個方法,讓我們可以引用這個臨時變數。這個方法就是所謂的右值引用。

那麼右值引用有什麼用呢?避免記憶體copy!

不同於其它語言,在c++里變數是值語義(在JAVA、Python變數是引用語義)。因此對於賦值操作意味著記憶體拷貝而不是簡單的賦值指針。而右值引用的一個作用就是我們可以通過重新利用臨時變數(右值)來避免無意義的記憶體copy。

 看這個例子(取自Rvalue References: C++0x Features in VC10, Part 2

1 string s0(“my mother told me that”);
2 string s1(“cute”);
3 string s2(“fluffy”);
4 string s3(“kittens”);
5 string s4(“are an essential part of a healthy diet”);
6 
7 string dest = s0 + ” ” + s1 + ” ” + s2 + ” ” + s3 + ” ” + s4;

 

第7行對string求和中,每次調用operator+()都會創建一個臨時變數,一共創建了8個臨時的string對象。而這裡真正低效的原因是,每次創建一個string對象都需要從堆上申請空間,將字元串的內容copy進來,並且這些臨時的string都是只用一次。

顯然這是低效的,為什麼不這樣子做呢:假設s0+""的時候創建的臨時變數是temp_str,此後我們一直是用這個臨時變數而不是重寫創建。這樣做表達式的結果是不會有任何改變,並且因為在opertor+()創建的都是程式其它地方不會引用到的臨時變數,所以這樣做也不會有任何副作用。相當於我們偷偷的做了個其它人無法察覺的小優化。

要如何做才能引用這個臨時的string對象呢?答案就是右值引用。通過右值引用我們就能重新利用表達式中的臨時變數,並且以更高效的指針swap來避免記憶體copy。

需要說明的一點是,右值引用優化的是避免對象在堆空間的記憶體的copy。在堆上的記憶體我們可以簡單的通過指針交換來傳遞記憶體資源的所有權(類似於vector的swap方法),而對於棧上的記憶體不可避免還是需要copy。舉一個例子這裡移動對象有點像放風箏:我放風箏,放著放著覺得累了就交給你,具體是怎麼交法呢?你先製作一個和我手上拿的一模一樣的手柄,接著我再把線剪短遞給你,你把線綁在你的手柄上,交接完畢!手柄對應是棧空間、風箏對應是堆空間。

這種技術十分有用,不僅僅是在處理臨時變數的時候起作用,有的時候我們想要使用這個轉移資源(記憶體)的效果時,也可以強制將類型轉為右值引用(std::move)來觸發對象移動。

舉一個例子,比如說對於vector的動態擴容。熟悉vector的實現的都知道,在對一個vector進行push_back時有可能會觸發記憶體的重新分配,這個時候需要把原來記憶體的對象copy到新分配的記憶體上,最後再釋放原來的記憶體。假設這個vector裡面存放的是string對象,那麼我們在執行簡單的對象賦值(調用的是string::operator=()方法)的過程中,我們copy的不僅僅是sizeof(string)的記憶體,我們還copy這個string內部指針指向堆空間上的記憶體。通過觀察可以發現,其實我們完全不必去拷貝內部指針指的那部分記憶體,因為原來的string對象在賦值完後就要被銷毀,如果我們將這個指針偷偷的拿過來(swap),程式的其它部分不會有任何察覺。為了實現這樣的操作我們需要做以下兩件事情:

  1. 對string實現一個移動構造函數、移動賦值函數。這些函數對內部指針進行swap操作,而不是copy操作。
  2. 通過std::move來強化轉化成右值引用,用以觸發移動賦值函數。編譯器正是通過參數類型是T&&,才知道應該使用移動版本的operator=()而不是copy版本的operator=()。
1 new_stri = std::move(old_str);

要對old_str轉化為右值引用是因為它並不是真正的右值,它不是一個臨時變數。但因為它即將被銷毀,所以效果等同於一個臨時變數。因此可以安全的轉換,從而調用移動賦值函數並悄悄的"移動"它的記憶體資源。

 

推薦閱讀:

  1. 知乎:如何理解C++中的move語義?邱昊宇的回答

參考資料:

  1. Visual C++ Team Blog:Rvalue References: C++0x Features in VC10, Part 2

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ASP.NET Core基於 .NET Core 項目模型,它支持構建能夠運行在 Windows、Mac和 Linux 上的跨平臺應用程式。當您構建一個 .Net Core 項目的時候,您可以選擇一種 .NET框架來構建您的應用程式,.NET Framework (CLR)、 .NET Core (... ...
  • LINQ對於筆者來說, 優美而濃縮的代碼讓人震驚. 研究LINQ就是在藝術化自己的代碼. 之前只是走馬觀花學會了基本的語法, 但是經常在CSDN看到令人驚訝自嘆不如的LINQ代碼, 還是讓人羡慕嫉妒恨, 真有一種心血來潮想追趕的衝動. 所以, 還是決定系統的學習一下LINQ. (1.4) LINQ ...
  • WCF如何實現對於Rest支持的呢?弄清這一點是學習Rest WCF的關鍵。 為了實現於對Rest的支持,在 .NET Framework 中,WCF 在 System.ServiceModel.Web 組件中新增了編程模型和一些基礎架構部件。WCF Web編程模型幾個重要類型就是: ...
  • 自古以來,人類的進步都是依賴於工具的進步,從刀耕火種,到使用青銅器,再到現在的科技,每一次都使我們的工作效率提高了無數倍,所以一個好的工具能使我們提高無數倍的工作效率,下麵,我就根據自己簡單的總結一下我們到底要有什麼樣的裝備。這裡現在只寫必須的,因為有太多的好的軟體了,以後再分類補充吧。 一,操作系 ...
  • 在WPF中使用依賴註入的方式創建視圖 0x00 問題的產生 互聯網時代桌面開發真是越來越少了,很多應用都轉到了瀏覽器端和移動智能終端,相應的軟體開發上的新技術應用到桌面開發的文章也很少。我之前主要做WPF,今年開始學習Web應用開發,於是就接觸到了.NET Core,其中的很多概念很值得在桌面開發中 ...
  • 昨天抽空寫了一個wcf的創建和宿主程式的創建文章,下麵也有很多園友給了評論,在此謝謝大家給了我繼續記錄我的摸爬滾打之路信心……抱拳! 上次的文章《我的WCF摸爬滾打之路(1)》中寫到,在測試wcf例子的時候遇到很多稀奇古怪的異常,準備列個專題寫的。無奈學習不深,實在不敢潦草為之。今天就隨便說說吧!說 ...
  • 本文介紹壓縮庫SharpZipLib的使用,提供封裝類的源代碼,以及測試UI的源代碼。 ...
  • 轉至:http://www.cnblogs.com/fonour/p/ajaxFileUpload.html 0、下載 http://files.cnblogs.com/files/fonour/ajaxfileupload.js 1、引用ajaxfileupload.js 2、頁面添加類型為fil ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...