【C++】——C++11新特性

目錄

前言

1.初始化列表

2.std::initializer_list

3.auto

4.decltype

5.nullptr

6.左值引用和右值引用?

6.1右值引用的真面目

6.2左值引用和右值引用比較

6.3右值引用的意義

6.3.1移動構造

6.4萬能引用

6.5完美轉發——forward

結語


前言

? C++,這門在系統開發、游戲開發、嵌入式等眾多領域都占據主導地位的編程語言,一直在不斷進化。C++11就像是C++家族中的一顆璀璨明星,閃耀著無數令人驚嘆的新特性。如果你還在使用傳統的C++編碼方式,那你可能正在錯過許多現代編程的便捷與高效。想象一下,用更少的代碼編寫出性能更高的程序,輕松應對復雜的并發場景,還能享受更加簡潔和安全的內存管理。C++11就能讓這一切成為現實。在這篇博文中,我會一點點揭開C++11新特性的神秘面紗,讓你看到C++在這次重大更新之后所蘊含的無限潛力。


1.初始化列表

在C++98標準中,允許使用 花括號{ } 對數組或者結構體元素進行統一的列表初始化

例如:

struct Point{int a;int b;
};int main(){int arry[] = {1,2,3,4};//數組初始化Point p = {1,2};//結構體初始化return 0;
}

而C++11擴大了使用 花括號{ } 進行初始化的使用范圍,使其可以用于所有的內置類型和用戶自定義的類型

//對數組初始化
int arry1[]{1,2,3,4,5};
//對內置類型初始化
int x{9};
//對用戶自定義類型初始化
Point p{9,9};
//對vector容器對象進行初始化
vector<int> vec = {1,2,3,4,5,6,7,8,9};

注意:

使用初始化列表時,可以添加等號 " = ",也可以不添加

2.std::initializer_list

initializer_list是什么?

看名字像一個容器,但我們學過的容器并沒有這個,但并非沒有見過

但C++11支持對各個容器進行初始化列表初始化,離不開它

std::initializer_list?是一個輕量級的模板類,用于在函數參數中傳遞初始化列表,從而使編譯器能夠正確解析和處理使用花括號?{}?進行初始化的對象。

我們在諸多容器的構造中,都可以看見它是身影

在vector構造中

在list構造中?

std::initializer_list?是定義在?<initializer_list>?頭文件中的一個模板類,用于表示一個初始化列表。

它的基本形式如下:

std::initializer_list<T> init_list = { /* 初始化元素 */ };

std::initializer_list?提供了一種輕量級的、只讀的視圖來訪問初始化列表中的元素。它常用于構造函數、函數參數以及返回類型,以支持統一初始化語法。

并不是所有的類型都會有std::initializer_list的構造函數,也并不是有std::initalizer_list才能使用花括號 { } 進行初始化

但只要你顯式寫了使用std::initializer_list的構造函數,則進行{ }初始化時,會優先調用該函數進行初始化

關于initializer_list的細節不過多贅述,知道有這個東西,和它的大致用途即可

3.auto

在C++98中auto是一個存儲類型的說明符,表明變量是局部自動存儲類型,但是局部域中定義局部的變量默認就是自動存儲類型,所以auto沒有什么價值

C++11廢棄auto的原本用法,將其用于實現自動類型推斷,這一功能極大地簡化了代碼編寫,特別是在處理復雜類型時。

舉個例子:

當一個類型很復雜時,可以使用auto進行簡寫

std::pair<int, std::string> get_user_info() {return {42, "Alice"};
}int main() {// 使用 auto 簡化返回類型的聲明auto user = get_user_info();reutrn 0;
}

4.decltype

在C++11標準中,decltype?是一個新增的關鍵字,用于在編譯時推導表達式的類型。與?auto?關鍵字類似,decltype?也用于類型推導,但兩者的工作機制和應用場景有所不同。

decltype?的基本語法

decltype(expression) variable_name;

說明:?

  • expression?是任何有效的C++表達式,decltype?會根據這個表達式的類型來推導出?variable_name?的類型。

示例:

int a = 5;
double b = 3.14;
decltype(a) c = a; // c 的類型為 int
decltype(b+b) d = b; // b+b表達式的結果類型為double,則b的類型為 double

注意:

decltype?不會實際計算表達式的值,它只在編譯時進行類型推導。

5.nullptr

由于C++中的NULL被定義為字面量0,但這樣可能會帶來一些問題

因為 NULL 既能表示指針常量,又能表示整形常量

出于清晰和安全的考慮,C++11新增了nullptr,用于表示空指針

6.左值引用和右值引用?

傳統的C++語法中就有引用的語法,而C++11中新增了的右值引用的語法特性

現在,引用就分為兩種

  1. 左值引用
  2. 右值引用

什么是左值,什么是左值引用?

左值是一個表示數據的表達式,如變量名、指針等

左值一般出現在賦值符號的左邊

//下面的指針p ,變量b、c 均為左值
int* p = new int(0);
int b =1;
const int c =2;

左值引用就是給左值的引用,給左值取別名

int*& rp = p;
int& rb = b;
const int& rc = c;

注意:

左值引用的類型一定是 左值類型+& ,必須嚴格匹配

什么是右值?什么是右值引用

右值也是一個表示數據的表達式,如字面常量、表達式返回值、函數返回值等等

右值只能出現在賦值符號的右邊

//常見右值
10;//常量
x+y;//表達式
add(x,y)//函數返回值

右值引用就是對右值的引用,給右值取別名

int&& rr1=10;
double&& rr2 = x + y;
double&& rr3 = fmin(x,y);

右值是不能取地址的,但給右值取別名后,會導致右值被存儲到特定的位置(一般是棧區),且可以取到該位置的地址

也就是說:不能取字面量10的地址,但rr1引用后,可以對rr1取地址,也可以修改rr1

誤區:

習慣使用左值引用后

我們對右值引用也會有一個誤解:可以通過修改右值引用來修改右值

這是不切實際的,我們修改的從來只是右值引用,而并非右值!

如上面的rr1,令rr1 = 11,并不會讓 10 = 11!

巧記

  1. 區分左值和右值:左值在 賦值操作符的左邊,右值在 賦值操作符的右邊
  2. 區分左值引用和右值引用:左值引用是 類型+&,右值引用是 類型+&&

6.1右值引用的真面目

右值不能被修改,這是公認的,例如?10 永遠不可能變成 11

但為什么被引用后,就可以被修改?10不是在常量區嗎?難道被移到棧區了?

右值引用一個右值,右值本身并沒有被“移動”到某個新的區域,而是右值引用的變量本身存儲了右值的內容

具體來說:

  • 右值引用的變量:右值引用本身是一個左值,它有自己的存儲位置(通常是棧上的某個位置)。這個變量存儲了右值的內容
  • 右值的原始位置:右值的本身的原始位置(如字符常量存儲在常量區)并不會因為右值引用而改變變。右值引用只是提供了一個訪問右值內容的途徑

右值引用的真面目——左值,左值可以被取地址,非const左值可以被修改

6.2左值引用和右值引用比較

左值引用總結:

  1. 左值引用不能引用左值,但不能引用右值
  2. const 左值引用可以引用左值,也可以引用右值

右值引用總結:

  1. 右值引用可以引用右值,不能引用左值,但可以引用move后的左值

6.3右值引用的意義

const 左值引用既可以引用左值又可以引用右值,那為什么C++11還要提出右值引用呢?

是不是畫蛇添足?

并不是,左值引用在一起場景下存在短板,而右值引用恰恰能解決這個短板

舉個例子:

先今,我們自定義了一個string類

該類中有一個拷貝構造函數,并在類外定義了一個to_string函數

兩者造型如下:

to_string函數

??????bit::string to_string(int value) {bit::string str;//return str;
}

string的拷貝構造函數

string(const string& s) : _str(nullptr) 
{        std::cout << "MyString(const MyString& s) -- 深拷貝" << std::endl;string tmp(s.str);swap(tmp);    
}

用to_string的返回值去構造一個string對象

?

這就體現左值引用的短板了

當函數返回對象是一個局部對象,出了函數作用域就不存在了,就會調用拷貝構造創建一個新的臨時對象,再用這個臨時對象去拷貝構造

一共會發生兩次拷貝構造,其中,創建臨時對象的時候,涉及開辟新的空間

開辟新空間,很浪費資源,太多次受不了

6.3.1移動構造

有沒有什么簡單,且不吃操作的方法規避深拷貝

有的,兄弟,有的

to_string的返回值是一個右值,用這個右值構造ret2,如果沒有移動構造,調用就會匹配調用拷貝構造,因為const左值引用是可以引用右值的,這里就是一個深拷貝

此時我們在bit::string中增加一個移動構造,就是用右值引用充當形參

具體造型如下

//移動構造
string(string&& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{swap(s);
}

此時再次構造ret2,如果既有拷貝構造,又有移動構造,則會調用更為匹配的移動構造

而移動構造中沒有新開空間,拷貝數據,效率提高

移動構造的本質:將參數右值的資源竊取過來,占為己有,就不用創建臨時對象,開辟空間,而是直接竊取別人的資源來構造自己。

為什么可以這樣呢?

右值引用可以延長右值的生命周期,使其生命周期向右值引用看齊

這樣函數中的返回值本來出了函數作用域就會被銷毀,回收資源,右值引用后,就延長了生命周期,不會被回收,

可以直接拿來構造新的對象,不用再創建臨時對象,造成額外開銷。

6.4萬能引用

“萬能引用”(Universal Reference)是 C++ 編程中的一個術語,主要用于描述一種能夠同時綁定到左值(lvalue)和右值(rvalue)的引用類型。

萬能引用通常出現在模板編程中,允許函數模板接受任意類型的參數,從而實現更靈活和通用的代碼設計。

來看以下例子

// 接受左值引用
void Fun(int& x) {std::cout << "Fun(int&): "<< x << std::endl;
}// 接受右值引用
void Fun(int&& x) {std::cout << "Fun(int&&): "<< x << std::endl;
}// 萬能引用的模板函數,調用相應的 Fun 函數
template<typename T>
void CallFun(T&& arg) {Fun(arg);
}

在main中進行調用模版函數

int a = 10;
const int b = 20;CallFun(a); // 傳遞左值,調用 Fun(int&)CallFun(30); // 傳遞右值,調用 Fun(int&&)

運行結果:

?

按理說,應該會調用一個 Fun(int&&) 和一個Fun(int&),但事實并不是這樣

為什么?

模版的萬能引用只是提供了能夠同時接收左值引用和右值引用的能力

但在后續的使用中,統一當成左值來使用

進入模版函數體中,只有左值,沒有右值

所以,會調用兩次 Fun(int&)

我們希望事情按照我們的期望發展

在模版函數體中,右值和左值也會保持原來的屬性,不會變化

這時候,就需要完美轉發

6.5完美轉發——forward

完美轉發是 C++11 引入的一項重要特性,旨在函數模板中將參數以其原始的值類別?(左值或右值)傳遞給其他函數,確保在轉發過程中不改變參數的性質。

基本造型:?

std::forward<T>(arg)

說明:

  • arg即為要保持原始值類比的對象

示例:

在萬能轉發的模版函數中,添加完美轉發

template<typename T>
void CallFun(T&& arg) {Fun(std::forward<T>arg);
}

main照常

int main() 
{int a = 10;const int b = 20;CallFun(a); // 傳遞左值,調用 Fun(int&)CallFun(30); // 傳遞右值,調用 Fun(int&&)return 0;
}

?運行一下:

和我們的預期一致

右值引用和左值引用各有各的用法,并不是隨意創造的,具體怎么用,需要結合實際情況進行分析選擇。


結語

? 綜上所述,C++11引入的初始化列表、右值引用以及nullptr等特性,為C++語言的發展注入了新的活力。初始化列表通過提供統一的初始化語法,增強了代碼的可讀性和可維護性;右值引用憑借其獨特的語義,極大地優化了對象的移動語義和資源管理效率;nullptr則為指針類型提供了一個明確而安全的表示。這些特性的引入,不僅豐富了C++語言的編程范式,更為現代軟件開發中的高效性、可靠性要求提供了有力支持,推動了面向對象編程向更高層次發展。

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

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

相關文章

【C++網絡編程】第5篇:UDP與廣播通信

一、UDP協議核心特性 1. UDP vs TCP ?特性 ?UDP?TCP連接方式無連接面向連接&#xff08;三次握手&#xff09;可靠性不保證數據到達或順序可靠傳輸&#xff08;超時重傳、順序控制&#xff09;傳輸效率低延遲&#xff0c;高吞吐相對較低&#xff08;因握手和確認機制&…

MOSN(Modular Open Smart Network)是一款主要使用 Go 語言開發的云原生網絡代理平臺

前言 大家好&#xff0c;我是老馬。 sofastack 其實出來很久了&#xff0c;第一次應該是在 2022 年左右開始關注&#xff0c;但是一直沒有深入研究。 最近想學習一下 SOFA 對于生態的設計和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概覽 SOFABoot-01-螞蟻金服開源的 s…

微信小程序日常開發問題整理

微信小程序日常開發問題整理 1 切換渲染模式1.1 WebView 的鏈接在模擬器可以打開&#xff0c;手機上無法打開。 1 切換渲染模式 1.1 WebView 的鏈接在模擬器可以打開&#xff0c;手機上無法打開。 在 app.json 中看到如下配置項&#xff0c;那么當前項目就是 keyline 渲染模式…

【Altium Designer】銅皮編輯

一、動態銅皮與靜態銅皮的區分與切換 動態銅皮&#xff08;活銅&#xff09;&#xff1a; 通過快捷鍵 PG 創建&#xff0c;支持自動避讓其他網絡對象&#xff0c;常用于大面積鋪銅&#xff08;如GND或電源網絡&#xff09;。修改動態銅皮后&#xff0c;需通過 Tools → Polygo…

Java「Deque」 方法詳解:從入門到實戰

Java Deque 各種方法解析&#xff1a;從入門到實戰 在 Java 編程中&#xff0c;Deque&#xff08;雙端隊列&#xff09;是一個功能強大的數據結構&#xff0c;允許開發者從隊列的兩端高效地添加、刪除和檢查元素。作為 java.util 包中的一部分&#xff0c;Deque 接口繼承自 Qu…

ffmpeg+QOpenGLWidget顯示視頻

?一個基于 ?FFmpeg 4.x? 和 QOpenGLWidget的簡單視頻播放器代碼示例&#xff0c;實現視頻解碼和渲染到 Qt 窗口的功能。 1&#xff09;ffmpeg庫界面&#xff0c;視頻解碼支持軟解和硬解方式。 硬解后&#xff0c;硬件解碼完成需要將數據從GPU復制到CPU。優先采用av_hwf…

深入解析嵌入式內核:從架構到實踐

一、嵌入式內核概述 嵌入式內核是嵌入式操作系統的核心組件&#xff0c;負責管理硬件資源、調度任務、處理中斷等關鍵功能。其核心目標是在資源受限的環境中提供高效、實時的控制能力。與通用操作系統不同&#xff0c;嵌入式內核通常具有高度可裁剪性、實時性和可靠性&#xff…

20250324-使用 `nltk` 的 `sent_tokenize`, `word_tokenize、WordNetLemmatizer` 方法時報錯

解決使用 nltk 的 sent_tokenize, word_tokenize、WordNetLemmatizer 方法時報錯問題 第 2 節的手動方法的法1可解決大部分問題&#xff0c;可首先嘗試章節 2 的方法 1. nltk.download(‘punkt_tab’) LookupError: *******************************************************…

『 C++ 』多線程同步:條件變量及其接口的應用實踐

文章目錄 條件變量概述條件變量簡介條件變量的基本用法 案例&#xff1a;兩個線程交替打印奇偶數代碼解釋 std::unique_lock::try_lock_until 介紹代碼示例代碼解釋注意事項 std::condition_variable::wait 詳細解析與示例std::condition_variable::wait 接口介紹代碼示例代碼解…

【VolView】純前端實現CT三維重建-CBCT

文章目錄 什么是CBCTCBCT技術路線使用第三方工具使用Python實現使用前端實現 純前端實現方案優缺點使用VolView實現CBCT VolView的使用1.克隆代碼2.配置依賴3.運行4.效果 進階&#xff1a;VolView配合Python解決卡頓1.修改VtkThreeView.vue2.新增Custom3DView.vue3.Python生成s…

debug - 安裝.msi時,為所有用戶安裝程序

文章目錄 debug - 安裝.msi時&#xff0c;為所有用戶安裝程序概述筆記試試在目標.msi后面直接加參數的測試 備注備注END debug - 安裝.msi時&#xff0c;為所有用戶安裝程序 概述 為了測試&#xff0c;裝了一個test.msi. 安裝時&#xff0c;只有安裝路徑的選擇&#xff0c;沒…

Java Stream兩種list判斷字符串是否存在方案

這里寫自定義目錄標題 背景初始化方法一、filter過濾方法二、anyMatch匹配 背景 在項目開發中&#xff0c;經常遇到篩選list中是否包含某個子字符串&#xff0c;有多種方式&#xff0c;本篇主要介紹stream流的filter和anyMatch兩種方案&#xff0c;記錄下來&#xff0c;方便備…

DeepSeek vs 通義大模型:誰將主導中國AI的未來戰場?

當你在深夜調試代碼時,是否幻想過AI伙伴能真正理解你的需求?當企業面對海量數據時,是否期待一個真正智能的決策大腦? 這場由DeepSeek和通義領銜的大模型之爭,正在重塑中國AI產業的競爭格局。本文將為你揭開兩大技術巨頭的終極對決! 一、顛覆認知的技術突破 1.1 改變游戲…

3. 軸指令(omron 機器自動化控制器)——>MC_SetOverride

機器自動化控制器——第三章 軸指令 12 MC_SetOverride變量?輸入變量?輸出變量?輸入輸出變量 功能說明?時序圖?重啟運動指令?多重啟動運動指令?異常 MC_SetOverride 變更軸的目標速度。 指令名稱FB/FUN圖形表現ST表現MC_SetOverride超調值設定FBMC_SetOverride_instan…

從像素到世界:自動駕駛視覺感知的坐標變換體系

接著上一篇 如何讓自動駕駛汽車“看清”世界?坐標映射與數據融合詳解的概述,這一篇詳細講解自動駕駛多目視覺系統設計原理,并給出應用示例。 摘要 在自動駕駛系統中,準確的環境感知是實現路徑規劃與決策控制的基礎。本文系統性地解析圖像坐標系、像素坐標系、相機坐標系與…

附錄B ISO15118-20測試命令

本章節給出ISO15118-20協議集的V2G命令&#xff0c;包含json、xml&#xff0c;并且根據exiCodec.jar編碼得到exi內容&#xff0c; 讀者可以參考使用&#xff0c;測試編解碼庫是否能正確編解碼。 B.1 supportedAppProtocolReq json: {"supportedAppProtocolReq": {…

VLAN章節學習

為什么會有vlan這個技術&#xff1f; 1.通過劃分廣播域來降低廣播風暴導致的設備性能下降&#xff1b; 2.提高網絡管理的靈活性和通過隔離網絡帶來的安全性&#xff1b; 3.在成本不變的情況下增加更多的功能性&#xff1b; VLAN又稱虛擬局域網&#xff08;再此擴展&#xf…

FPGA時鐘約束

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 目錄 前言 一、Create_clock 前言 時鐘周期約束&#xff0c;就是對時鐘進行約束。 一、Create_clock create_clock -name <name> -period <period> -waveform …

機房布局和布線的最佳實踐:如何打造高效、安全的機房環境

機房布局和布線的最佳實踐:如何打造高效、安全的機房環境 大家好,我是Echo_Wish。今天我們來聊聊機房布局和布線的問題,這可是數據中心和IT運維中的一個非常重要的環節。不管是剛剛接觸運維的新人,還是已經摸爬滾打多年的老兵,都應該對機房的布局和布線有一個清晰的認識。…

spring-security原理與應用系列:建造者

目錄 1.構建過程 AbstractSecurityBuilder AbstractConfiguredSecurityBuilder WebSecurity 2.建造者類圖 SecurityBuilder ???????AbstractSecurityBuilder ???????AbstractConfiguredSecurityBuilder ???????WebSecurity 3.小結 緊接上一篇文…