【C++進階學習】第十一彈——C++11(上)——右值引用和移動語義

前言:

前面我們已經將C++的重點語法講的大差不差了,但是在C++11版本之后,又出來了很多新的語法,其中有一些作用還是非常大的,今天我們就先來學習其中一個很重要的點——右值引用以及它所擴展的移動定義

目錄

一、左值引用和右值引用

左值引用?

右值引用

二、左值引用與右值引用的比較

三、右值引用的使用

移動構造

移動賦值

四、總結


一、左值引用和右值引用

左值引用?

左值引用是最常見的引用類型,通常用于綁定到一個左值。左值是一個具有名稱的對象,可以取地址,通常出現在賦值操作符的左邊。(簡單的說,能取地址的就是左值)

語法:

類型 &引用名 = 左值;

示例:

int a = 10;
int &refA = a;  // refA是一個左值引用,綁定到左值a

特點:

  • 左值引用必須初始化,并且只能綁定到左值。
  • 左值引用可以修改綁定的對象。

右值引用

右值引用是C++11引入的新特性,用于綁定到一個右值。右值是一個臨時對象,通常沒有名稱,不能取地址,通常出現在賦值操作符的右邊。(右值不能取地址,比如常量)

語法:

類型 &&引用名 = 右值;

示例:

int &&refB = 20;  // refB是一個右值引用,綁定到右值20

特點:

  • 右值引用必須初始化,并且只能綁定到右值。
  • 右值引用主要用于實現移動語義和完美轉發。

有一個需要強調的是,常變量雖然也屬于常量,但是它可以取地址,所以它屬于左值

二、左值引用與右值引用的比較

左值引用:

1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值
int main()
{// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a; ? // ra為a的別名//int& ra2 = 10; ? // 編譯失敗,因為10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;
}

右值引用:

1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。
int main()
{// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 無法從“int”轉換為“int &&”// message : 無法將左值綁定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return 0;
}

三、右值引用的使用

在上面我們也已經講到了,左值引用及可以引用左值,又可以引用右值,那么C++11為什么還要設計右值引用呢?下面我們來看一下原因。

我們借助string類來講解

先來看一下下面所出現的所有代碼,可以先思考看看思考思考

namespace zda
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷貝構造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷貝" << endl;string tmp(s._str);swap(tmp);}// 賦值重載string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷貝" << endl;string tmp(s);swap(tmp);return *this;}// 移動構造string(string&& s)   //右值引用:_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移動語義" << endl;swap(s);}// 移動賦值string& operator=(string&& s)    //右值引用{cout << "string& operator=(string&& s) -- 移動語義" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做標識的\0};string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}

左值引用使用場景:

void func1(bit::string s)
{}
void func2(const bit::string& s)
{}
int main()
{bit::string s1("hello world");// func1和func2的調用我們可以看到左值引用做參數減少了拷貝,提高效率的使用場景和價值func1(s1);func2(s1);// string operator+=(char ch) 傳值返回存在深拷貝// string& operator+=(char ch) 傳左值引用沒有拷貝提高了效率s1 += '!';return 0;
}

左值引用短板:

當函數返回對象為臨時變量的時候,左值引用就派不上用場了,就只能傳值返回,就需要拷貝至少一次(老一點的編譯器為兩次)

右值引用和移動語義:
對于上面這種問題,我們就可以通過右值引用和移動語義來實現

移動構造

移動構造的本質就是將參數的右值竊取過來,占為己有,這樣它就不用再深度拷貝了,所以叫做移動構造
// 移動構造
string(string&& s):_str(nullptr),_size(0),_capacity(0)
{cout << "string(string&& s) -- 移動語義" << endl;swap(s);
}
int main()
{zda::string ret2 = bit::to_string(-1234);return 0;
}

當返回值是右值時,因為移動構造并沒有開辟空間進行深拷貝,所以效率就會更高

需要注意的是,當拷貝構造和移動構造同時存在時,編譯器默認的也會調用移動構造,因為編譯器會默認調用效率更高的函數

移動賦值

// 移動賦值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移動語義" << endl;
swap(s);
return *this;
}
int main()
{zda::string ret1;ret1 = zda::to_string(1234);return 0;
}// 運行結果:
// string(string&& s) -- 移動語義
// string& operator=(string&& s) -- 移動語義

這里運行后發現,調用了一次移動構造和一次移動賦值,因為這里的ret1是一個已經存在的對象,用它來接受函數返回值的時候編譯器就無法再優化了,所以會在移動構造后創建一個臨時變量,且這個臨時變量會被編譯器識別為右值,從而調用移動賦值

四、總結

上面我們就簡單的先提了一下右值引用的應用:移動語義,下一篇我們再重點講解一下右值引用的另一個重點語法:完美揮發

感謝各位大佬觀看,創作不易,還請各位大佬點贊支持一下!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/92909.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/92909.shtml
英文地址,請注明出處:http://en.pswp.cn/web/92909.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【IoTDB】363萬點/秒寫入!IoTDB憑何領跑工業時序數據庫賽道?

【作者主頁】Francek Chen 【專欄介紹】???大數據與數據庫應用??? 大數據是規模龐大、類型多樣且增長迅速的數據集合&#xff0c;需特殊技術處理分析以挖掘價值。數據庫作為數據管理的關鍵工具&#xff0c;具備高效存儲、精準查詢與安全維護能力。二者緊密結合&#xff0…

IEEE 2025 | 重磅開源!SLAM框架用“法向量+LRU緩存”,將三維重建效率飆升72%!

一、前言 當前研究領域在基于擴散模型的文本到圖像生成技術方面取得了顯著進展&#xff0c;尤其在視覺條件控制方面。然而&#xff0c;現有方法&#xff08;如ControlNet&#xff09;在組合多個視覺條件時存在明顯不足&#xff0c;主要表現為獨立控制分支在去噪過程中容易引入…

無人機遙控器教練模式技術要點

一、技術要點1.控制權仲裁機制&#xff1a;核心功能&#xff1a;清晰定義主控權歸屬邏輯&#xff08;默認為學員&#xff0c;但教練隨時可接管&#xff09;。切換方式&#xff1a;通常通過教練遙控器上的物理開關&#xff08;瞬時或鎖定型&#xff09;或軟件按鈕觸發。切換邏輯…

【跨服務器的數據自動化下載--安裝公鑰,免密下載】

跨服務器的數據自動化下載功能介紹&#xff1a;上代碼&#xff1a;發現好久沒寫csdn了&#xff0c;說多了都是淚~~ 以后會更新一些自動化工作的腳本or 小tricks&#xff0c;歡迎交流。分享一個最近在業務上寫的較為實用的自動化腳本&#xff0c;可以批量從遠端服務器下載指定數…

C++-->stl: list的使用

前言list的認識list是可以在固定時間&#xff08;O&#xff08;1&#xff09;&#xff09;內在任意位置進行插入和刪除的序列式容器&#xff0c;并且該容器可以前后雙向迭代。 2. list的底層是雙向鏈表結構&#xff0c;雙向鏈表中每個元素存儲在互不相關的獨立節點中&#xff0…

本地WSL部署接入 whisper + ollama qwen3:14b 總結字幕

1. 實現功能 M4-1 接入 whisper ollama qwen3:14b 總結字幕 自動下載視頻元數據如果有字幕&#xff0c;只下載字幕使用 ollama 的 qwen3:14b 對字幕內容進行總結 2.運行效果 source /root/anaconda3/bin/activate ytdlp &#x1f50d; 正在提取視頻元數據… &#x1f4dd; 正在…

《Linux運維總結:Shell腳本高級特性之變量間接調用》

總結&#xff1a;整理不易&#xff0c;如果對你有幫助&#xff0c;可否點贊關注一下&#xff1f; 更多詳細內容請參考&#xff1a;Linux運維實戰總結 一、變量間接調用 在Shell腳本中&#xff0c;變量間接調用是一種高級特性&#xff0c;它允許你通過另一個變量的值來動態地訪問…

ABP VNext + Akka.NET:高并發處理與分布式計算

ABP VNext Akka.NET&#xff1a;高并發處理與分布式計算 &#x1f680; 用 Actor 模型把高并發寫入“分片→串行化”&#xff0c;把鎖與競態壓力轉回到代碼層面的可控順序處理&#xff1b;依托 Cluster.Sharding 橫向擴容&#xff0c;Persistence 宕機可恢復&#xff0c;Strea…

[激光原理與應用-250]:理論 - 幾何光學 - 透鏡成像的優缺點,以及如克服缺點

透鏡成像是光學系統中應用最廣泛的技術&#xff0c;其通過折射原理將物體信息轉換為圖像&#xff0c;但存在像差、環境敏感等固有缺陷。以下是透鏡成像的優缺點及針對性改進方案&#xff1a;一、透鏡成像的核心優點高效集光能力透鏡通過曲面設計將分散光線聚焦到一點&#xff0…

測試匠談 | AI語音合成之大模型性能優化實踐

「測試匠談」是優測云服務平臺傾心打造的內容專欄&#xff0c;匯集騰訊各大產品的頂尖技術大咖&#xff0c;為大家傾囊相授開發測試領域的知識技能與實踐&#xff0c;讓測試工作變得更加輕松高效。 本期嘉賓介紹 Soren&#xff0c;騰訊TEG技術事業群質量工程師&#xff0c;負責…

用天氣預測理解分類算法-從出門看天氣到邏輯回歸

一、生活中的決策難題&#xff1a;周末郊游的「天氣判斷」 周末計劃郊游時&#xff0c;你是不是總會打開天氣預報反復確認&#xff1f;看到 "25℃、微風、無雨" 就興奮收拾行李&#xff0c;看到 "35℃、暴雨" 就果斷取消計劃。這個判斷過程&#xff0c;其…

HTTPS服務

HTTPS服務 一、常見的端口 http ------ 80 明文 https ------ 443 數據加密 dns ------ 53 ssh ------ 22 telent ------ 23 HTTPS http ssl或者tls &#xff08;安全模式&#xff09; 二、原理&#xff1a; c&#xff08;客戶端…

【Android筆記】Android 自定義 TextView 實現垂直漸變字體顏色(支持 XML 配置)

Android 自定義 TextView 實現垂直漸變字體顏色&#xff08;支持 XML 配置&#xff09; 在 Android UI 設計中&#xff0c;字體顏色的漸變效果能讓界面看起來更加精致與現代。常見的漸變有從左到右、從上到下等方向&#xff0c;但 Android 的 TextView 默認并不支持垂直漸變。…

CANopen Magic調試軟件使用

一、軟件安裝與硬件連接1.1 系統要求操作系統&#xff1a;Windows 7/10/11 (64位)硬件接口&#xff1a;支持Vector/PEAK/IXXAT等主流CAN卡推薦配置&#xff1a;4GB內存&#xff0c;2GHz以上CPU1.2 安裝步驟運行安裝包CANopen_Magic_Setup.exe選擇安裝組件&#xff08;默認全選&…

前端css學習筆記3:偽類選擇器與偽元素選擇器

本文為個人學習總結&#xff0c;如有謬誤歡迎指正。前端知識眾多&#xff0c;后續將繼續記錄其他知識點&#xff01; 目錄 前言 一、偽類選擇器 1.概念 2.動態選擇器&#xff08;用戶交互&#xff09; 3.結構偽類 &#xff1a;first-child&#xff1a;選擇所有兄弟元素的…

深入探索 PDF 數據提取:PyMuPDF 與 pdfplumber 的對比與實戰

在數據處理和分析領域&#xff0c;PDF 文件常常包含豐富的文本、表格和圖形信息。然而&#xff0c;從 PDF 中提取這些數據并非易事&#xff0c;尤其是當需要保留格式和顏色信息時。幸運的是&#xff0c;Python 社區提供了多個強大的庫來幫助我們完成這項任務&#xff0c;其中最…

Springboot注冊過濾器的三種方式(Order 排序)

一、使用 Component Order&#xff08;簡單但不夠靈活&#xff09; 適用于全局過濾器&#xff0c;無需手動注冊&#xff0c;Spring Boot 會自動掃描并注冊。 Component Order(1) // 數字越小&#xff0c;優先級越高 public class AuthFilter implements Filter {Autowired /…

電腦硬件詳解

前幾天我的風扇轉的很快&#xff0c;而且cpu占用率很高&#xff0c;然后我在想怎么回事&#xff0c;然后就淺淺研究了一下電腦的硬件。 筆記本主板&#xff1a; 臺式機主板&#xff1a; 圖1&#xff1a; 圖2&#xff1a; 電腦硬件詳解 電腦的硬件是組成計算機系統的物理設…

力扣47:全排列Ⅱ

力扣47:全排列Ⅱ題目思路代碼題目 給定一個可包含重復數字的序列 nums &#xff0c;按任意順序 返回所有不重復的全排列。 思路 又是任意順序和所有不重復的排列&#xff0c;顯而易見我們要使用回溯的辦法。 首先是回溯的結束條件即新數組的長度等于nums的長度。這道題的難點…

學習筆記091——如何實現web登錄時,密碼復雜度校驗?(后端)

1、創建工具類 /*** 密碼復雜度校驗* param password 密碼*/ public static void validatePassword(String password) {// 至少8位if (password.length() < 8) {throw new IllegalArgumentException("密碼長度至少為8位");}// 包含大小寫字母if (!password.matche…