c++ 右值引用,move關鍵字

来源:https://www.cnblogs.com/xiaoshiwang/archive/2018/09/04/9582325.html
-Advertisement-
Play Games

c++ move關鍵字 move的由來:在 c++11 以前存在一個有趣的現象:T& 指向 lvalue (左傳引用), const T& 既可以指向 lvalue 也可以指向 rvalue。但卻沒有一種引用類型,可以限製為只指向 rvalue。 就這麼簡單!你甚至可以暫時想像它的原型是這樣的(當然 ...


c++ move關鍵字

move的由來:在 c++11 以前存在一個有趣的現象:T&  指向 lvalue (左傳引用), const T& 既可以指向 lvalue 也可以指向 rvalue。但卻沒有一種引用類型,可以限製為只指向 rvalue。

c++11 中的 move() 是這樣一個函數,它接受一個參數,然後返回一個該參數對應的右值引用.

就這麼簡單!你甚至可以暫時想像它的原型是這樣的(當然是錯的)

T&& move(T& val);

&&的由來:在函數體中,程式員無法分辨傳進來的參數到底是不是 rvalue,我們缺少一個 rvalue 的標記。為瞭解決這個問題,c++11 中引入了一個新的引用類型: some_type_t &&,這種引用指向的變數是個 rvalue。

由於&和&&是屬於不同的類型,所以用於各種函數(構造函數,賦值函數)的重載

holder(holder& other)
holder(holder&& other)

上面是2個重載函數

holder& operator=(holder& other)
holder& operator=(holder&& other)

上面是2個重載函數

具體看下麵的例子:假設我們有一個類,它包含了一些資源。我們的願望是:當調用拷貝構造函數或者賦值語句時,我們不想再new一個Resource的對象,因為要new一個Resource對象,即浪費空間,有浪費時間。

解決辦法:利用右值引用。

右值,本質上是一個臨時的記憶體空間,使用過後,系統馬上就會釋放掉它,有了右值引用後,就可以延長這個臨時空間的生命周期,相當於有了左值的效果,所以可以使用這個臨時空間了。回到上面提出的問題,我們不想再new一個Resource的對象,這時我們就可以利用右值引用,去引用一個臨時的並且馬上要被釋放的空間。

為了調用&&的拷貝構造函數,必須使用std::move,轉化成右值引用.

holder h2(std::move(get_holer()))

但是,h1 = get_holer(),即使不使用std::move,也會調用&&的賦值函數

h1 = get_holer()

完整代碼:

#include <iostream>
using namespace std;

class Resource{};

class holder{
public:
  //構造函數                                                       
  holder(){res = new Resource();}
  //析構函數                                                       
  ~holder(){
    //res不為NULL,就釋放res                                     
    if (res)delete res;
  }
  //拷貝構造函數                                                   
  holder(const holder& other){
    cout << "holder&" << endl;
    res = new Resource(*other.res);
  }
  holder(holder& other){
    cout << "holder&1" << endl;
    res = new Resource(*other.res);
  }

  //右值                                                           
  holder(holder&& other){
    cout << "holder&&" << endl;
    res = other.res;
    other.res = nullptr;
  }

  //賦值                                                           
  holder& operator=(const holder& other){
    cout << "operator" << endl;
    delete res;
    res = new Resource(*other.res);
    return *this;
  }
  holder& operator=(holder& other){
    cout << "operator1" << endl;
    delete res;
    res = new Resource(*other.res);
    return *this;
  }

  //右值                                                           
  holder& operator=(holder&& other){
    cout << "operator &&" << endl;
    std::swap(res, other.res);
    return *this;
  }

private:
  Resource* res;
};

holder get_holer(){
  holder h;
  return h;
}

int main(void){
  holder h1,h11;
  holder h2(std::move(get_holer()));//調用holder(holder&& other)
  holder h3(get_holer());//編譯器自動優化了,沒有調用拷貝構造函數  
  holder h4(h11);

  h1 = h2;//調用operator(holder&);                                 
  h1 = get_holer();//調用operator(holder&&);                        

}

上面的例子有個需要註意的地方,就是下麵這行代碼

holder h3(get_holer());//編譯器自動優化了,沒有調用拷貝構造函數  

這行代碼乍一看,應該調用拷貝構造函數。但是實際用GDB,斷點調試時,發現並沒有調用拷貝構造函數。

個人的猜測:如果編譯器不優化,在函數get_holer()的return一行處就應該有一次拷貝,調用拷貝構造函數,把h拷貝一份返回給調用測,然後由於holder h3(get_holer())的寫法,又要調用一次拷貝構造函數,把get_holer()返回值拷貝給h3。這樣一來就多了2次不必要的拷貝,所以編譯器自動優化,把這2次拷貝構造函數的調用都省略掉了,直接讓h3 = 在get_holer()創建的對象

65        holder h2(std::move(get_holer()));                       
(gdb) n  
holder&& 
66        holder h3(get_holer());//編譯器自動優化了,沒有調用拷貝構造函數 
(gdb) s  
get_holer () at rvalue_move.cpp:59                                 
59        holder h;//調用一次構造函數,在下麵的9行可以看到                                                
(gdb) s                                                            
holder::holder (this=0x7fffffffe180) at rvalue_move.cpp:9 
9         holder(){res = new Resource();}  //調用構造函數,創建h對象
(gdb) s 
get_holer () at rvalue_move.cpp:60 
60        return h; //返回h對象
(gdb) p h  //查看h對象裡面res的記憶體地址
$12 = {res = 0x603070} 
(gdb) p &h //查看h的記憶體地址
$13 = (holder *) 0x7fffffffe180  
(gdb) n       
61      } 
(gdb) n  
main () at rvalue_move.cpp:67  
(gdb) p h3 //查看h3對象裡面res的記憶體地址,發現h3的res的記憶體地址和在函數get_holer()里創建的h對象的res的記憶體地址相同
$14 = {res = 0x603070}                                             
(gdb) p &h3 //查看h3的記憶體地址後,發現h3的記憶體地址和在函數get_holer()里創建的h對象的記憶體地址相同
$15 = (holder *) 0x7fffffffe180

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

-Advertisement-
Play Games
更多相關文章
  • 最近的一段時間一直在搞 ,一個巨硬出品、賦予 語言靜態類型和編譯的語言。 第一個完全使用 重構的純 項目已經上線並穩定運行了。 第二個前後端的項目目前也在重構中,關於前端基於 的`TypeScript`套路之前也有提到過: "TypeScript在react項目中的實踐" 。 但是這些做完以後也總感 ...
  • 下載地址: https://github.com/imxiaoer/FloatToolBar 因為是個常見的功能,所以寫個組件。效果圖如下: 組件具體代碼如下: tool.vue 下載地址: https://github.com/imxiaoer/FloatToolBar ...
  • 幾米圈官網8個頁麵包括路由的配置在vue腳手架中進行開發,主要使用bootstrap完成頁面的佈局,amazeui完成動畫效果。vue腳手架單頁面開發路由切換其他子頁面主要遇到導入js和css的問題。在全局導入js插件時應該使用npm下載當前插件,在局部導入時,如果涉及到對現有界面中dom元素事件的 ...
  • 需求: 利用MySql資料庫結合前端技術完成用戶的註冊(要求不使用Web服務技術),所以 Demo採用Socket技術實現Web通信. 第一部分:資料庫創建 資料庫採用mysql 5.7.18, 資料庫名稱為MyUser, 內部有一張表 user.欄位有 Id,UserName,Psd,Tel 第二 ...
  • 1、event.stopPropagation 停止事件的傳播,阻止它被分配到其它Dom節點。但是不能阻止同一Dom節點上的其它事件句柄被調用。 2、event.preventDefault 阻止與事件關聯的預設動作。 ...
  • 本文轉自:http://developer.51cto.com/art/201709/552085.htm 本文轉自:https://www.cnblogs.com/stulzq/p/8573828.html 微服務架構現在是談到企業應用架構時必聊的話題,微服務之所以火熱也是因為相對之前的應用開發方 ...
  • 不管乾什麼,設定一個目標,針對一個目標有一個核心戰略,並堅決的執行核心戰略是取得勝利的不二法寶。 舉個慄子🌰: 三國三分天下。魏蜀吳都有自己的階段性核心戰略。魏國曹操的戰略是挾天子以令諸侯。東吳孫權的戰略是依靠天險,有水做天然屏障,孫吳水師一家獨大。蜀國的戰略是東聯孫權,北拒曹操。 魏國和吳國的執 ...
  • 前言 剛從事開發那段時間不習慣輸出日誌,認為那是無用功,徒增代碼量,總認為自己的代碼無懈可擊;老大的叮囑、強調也都視為耳旁風,最終導致的結果是我加班排查問題,花的時間還挺長的,要復現問題、排查問題等,幸虧那是公司內部員工用的系統,時間長一點也沒什麼大問題,但是如果是針對客戶的,時間就代表很多東西了, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...