【C++】C++11新特性(一)

文章目錄

  • 列表初始化
  • initializer_list
  • 左值引用和右值引用

列表初始化

在 C++98 中可以使用{}對數組或者結構體元素進行統一的列表初始值設定

struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0; 
}

C++11擴大了用大括號括起的列表(初始化列表)的使用范圍,使其可用于所有的內置類型和用戶自定義的類型,使用列表初始化時,可添加等號(=),也可不添加

列表初始化可以防止窄化轉換。窄化轉換是指可能導致數據丟失或精度降低的轉換。例如,將一個浮點數轉換為整數時,如果浮點數的小數部分非零,就會丟失小數部分的數據。如果嘗試進行窄化轉換,編譯器會報錯

一切皆可用列表初始化,可以省略等號

class A
{
public:A(int a):_a(a){}A(int a,int b):_a(a),_b(b){}
private:int _a;int _b;
}
A a1{1};
A a2{11,45};
//本質上就是多參數的隱式類型轉換
//在C++98單參數隱式類型轉換的基礎上引入了多參數的隱式類型轉換
int arr[]{12,34,56,78,90};

創建對象時也可以使用列表初始化方式調用構造函數初始化

本質是生成一個 A 的臨時對象,然后將這個臨時對象賦給變量,編譯器優化后就變成了隱式類型轉換,可以通過在類前面加上關鍵字 explicit 來阻止隱式類型轉換

A& a1{12};//臨時對象具有常性,A&對象不能引用
const A& a2{12};

構造生成一個臨時對象,臨時對象具有常性,可以賦值給const變量

A a1=11;//單參數的隱式類型轉換
A a2={11,12};//多參數的隱式類型轉換,實質上是構造了一個A對象,然后調用了拷貝構造傳給a2對象

initializer_list

本質是個常量數組,里面只存有指向first和last的指針,因此32位下只有8字節,支持迭代器,因此可以遍歷

統一容器初始化方式:標準容器(如std::vectorstd::list等)都支持使用std::initializer_list進行初始化。這使得容器的初始化方式更加統一和直觀。也可以作為operator=的參數,這樣就可以用大括號賦值

如果想要自定義類型支持列表初始化,就可以在自定義類型的構造函數中使用std::initializer_list

std::initializer_list是一個不可變的類型,即一旦創建,它的元素不能被修改。它提供了beginend函數來訪問其中的元素,就像訪問數組一樣,但不提供修改元素的接口

vector(const T& x1);
vector(const T& x1,const T&x2);
...
vector(initializer_list<T> il);
//這個構造一勞永逸的解決了問題,不用一個個寫構造
vector(initializer_list<T>& il)
{vector(std::initializer_list<T> il)//可以不用加引用,和const原因:這里的initializer_list本來要的就是淺拷貝//實質上是il的指針分別指向常量數組的第一個元素和常量數組的最后一個元素,不用拷貝,直接就構造了,因此加上const和引用對性能提升沒有影響//{1,2,3,4,5,6,78,75}
{reserve(il.size());for (auto& e : il){push_back(e);}
}
}
vector<int> v1={1,5,6,8,6,4}//隱式類型轉換
vector<int> v2({1,5,5,6,9,1})//構造函數
map<int,int> m1={{1,2},{2,3},{3,4}};
//實際上是生成了隱式類型轉換成了pair對象,然后再使用initializer_list<pair>進行構造

中間實際生成了臨時對象,然后拷貝構造,實質也是隱式類型轉換

實質是pair多參數的隱式類型轉換和initializer_list

decltype

與typeid類似,但decltype推斷出的類型可以定義變量,也可以用來模板傳參,而typeid不行,typeid 只是一個字符串,不能用于定義對象

list<int>::iterator it1;
cout << typeid(it1).name();
//結果:class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > >
typeid(it1).name() it2;//錯誤,typeid推出的類型不能用于定義變量,只是一個字符串
decltype(it1) it2;
cout << typeid(it2).name();
//結果:class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > >
//用于賦值的類型和原類型一致

decltype 的通常用于推導 auto 的類型

class A
{
public:T* func(){return new T;}private:int _a=10;
};
auto func()
{list<int> l;return l.begin();
}
auto i = func();
A<decltype(i)> a;

decltype 和 auto 的使用會增加代碼的閱讀難度

左值引用和右值引用

左值和右值的區分:

左值可以取地址,右值不能取地址

匿名對象不能取地址,是右值

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

右值引用:給右值取別名,常見右值: 字面常量、表達式返回值,函數返回值

左值是一個表示數據的表達式(如變量名或解引用的指針),我們可以獲取它的地址+可以對它賦值,左值可以出現賦值符號的左邊,右值不能出現在賦值符號左邊。定義時const修飾符后的左值,不能給他賦值但是可以取它的地址

左值引用和右值引用都是給對象起別名

純右值(內置類型的)

將亡值(自定義類型的)

string&& ref1 = string("123");
string&& ref2 = to_string(123);
int&& ref3 = 10;
int&& ref4=(x+y);

左值引用不可以給右值起別名,但const修飾的左值可以

右值引用不可以給左值起別名,但是可以給move以后的左值起別名

const int& leftref = 10;
int a = 10;
int&& rightref = move(a);

move 只是會將左值強轉為右值,但并不會涉及到資源的分配,只是告訴編譯器可以進行右值操作,允許將這個變量的資源進行分配

被 move 之后的左值,一旦被分配,則自己就不再擁有原來的資源,而是分配出去了,就像真正的右值一樣

引用的意義是減小拷貝,提高效率

左值引用沒有徹底解決這個問題,因為局部變量無法引用

移動構造:傳入右值引用的構造,將傳入的右值的資源剝奪后分配給要構造的對象,避免了拷貝造成的資源占用

如果是右值,那么直接把資源轉移

可以直接返回局部本來要銷毀的變量,不用拷貝構造

移動構造本質是將參數右值的資源竊取過來,占位已有,那么就不用做深拷貝了,所以它叫做移動構造,就是竊取別人的資源來構造自己

string func()
{string s="12456";return ret;//return move(ret);編譯器自動優化成右值
}
string ret=func();
//這里由于編譯器優化,會將原本ret需要拷貝構造一個臨時對象
//然后ret通過移動構造將這個臨時對象的資源拿到,優化為直接進行移動構造
//相當于將ret強行識別為右值

純右值: 內置類型,返回類型為非引用類型的函數調用或運算符表達式屬于純右值,lambda 表達式為純右值, 因為表達式本身沒有名字,本質是臨時值,例如 42、a + b 或 func()(函數返回非引用類型)
將亡值: 自定義類型,返回類型為對象右值引用的表達式為將亡值,右值類對象的成員為將亡值,右值數組的成員為將亡值,標識一個具名對象,但該對象即將被銷毀(如通過 std::move 轉換)。它允許安全地移動資源,而非拷貝

移動語義允許高效地轉移資源,避免不必要的復制,特別是對于大型或資源密集型的對象,當一個變量或者右值被移動后,資源將會被分配到其他位置,原來的變量或右值不能夠訪問

**移動賦值:**移動賦值運算符(operator=的移動賦值版本)用于將一個對象的資源轉移到另一個已經存在的對象中。與移動構造函數類似,它的主要目的是高效地處理資源的轉移,避免不必要的資源復制。

移動賦值運算符通常也期望右值引用作為參數。當有右值(如臨時對象)出現在賦值表達式的右側時,編譯器會優先選擇移動賦值運算符(如果定義了的話)來進行資源的轉移。

如果想對左值進行移動賦值操作,可以像移動構造函數一樣,使用std::move函數將左值轉換為右值引用

不能被移動的左值

  1. 常量對象(const 左值)

移動操作需要修改源對象,常量禁止對象修改,嘗試移動常量對象會調用拷貝而非移動

const std::string s = "Hello";
std::string s2 = std::move(s);
// 調用拷貝構造函數,s中的值還是Hello,說明沒有移動,只是進行了拷貝
  1. 基本數據類型的對象(int,float,double)

沒有動態資源,移動等同于拷貝

int a = 10;
int&& rightref = move(a);//a中的值還是10,說明只是拷貝,并沒有移動
  1. 無移動操作的類對象

若類未定義移動構造函數/賦值運算符,或編譯器未隱式生成,則移動會退化為拷貝

class NoMove {
public:NoMove(){cout << "constructor function" << endl;}NoMove(const NoMove&){cout << "copy constructor" << endl;} // 只有拷貝構造函數
};
int main()
{NoMove obj1;NoMove obj2 = std::move(obj); // 調用拷貝構造函數
}
  1. 移動操作被顯式刪除的類對象

若移動構造函數/賦值運算符被標記為= delete,嘗試移動會引發編譯錯誤

class DeletedMove {
public:DeletedMove(){}DeletedMove(DeletedMove&&) = delete;
};
DeletedMove dm1;
DeletedMove dm2 = std::move(dm); // 編譯錯誤
  1. 包含不可移動成員的類對象

若類的成員或基類不可移動,隱式移動操作會被刪除,導致只能拷貝

struct NonMovableMember {NonMovableMember(NonMovableMember&&) = delete;
};
class Wrapper {NonMovableMember m;
};
Wrapper w;
Wrapper w2 = std::move(w); // 隱式移動被刪除,嘗試調用拷貝構造函數
struct S { int i : 4; };
S s;
int x = std::move(s.i); // 實際拷貝位域的值

右值引用可以引用字面常量int&& a=5,這里的 a 實際上是 const int&& 類型,因此不能通過 a 修改這個值

右值引用還能引用表達式產生的臨時對象,如,在函數調用返回一個臨時對象時,這個臨時對象是右值,可以用右值引用綁定它

  • 假設存在一個函數std::vector<int> createVector(),它返回一個std::vector<int>對象。可以這樣使用右值引用:std::vector<int>&& v = createVector();。這里createVector返回的臨時vector對象被右值引用v綁定
  • 另一個例子是算術表達式的結果,如int&& result = (3 + 5);。表達式(3 + 5)產生一個臨時的int8,右值引用result綁定了這個臨時值

在移動語義的場景下,右值引用用于引用那些即將被移動資源的對象,當一個對象要將自己的資源(如動態分配的內存)轉移給另一個對象時,會使用右值引用

右值引用本身是左值,只有右值本身處理成左值,才能實現移動構造,因此右值引用的函數要將接收到的右值傳給下一個函數時,要進行 move,才能調用下一個函數的 move 形式

如果右值引用的屬性是右值,那么移動構造和移動賦值,要轉移的語法邏輯是矛盾的,因為右值無法被改變(可以理解為右值默認自帶 const 屬性)因此右值引用本身要被處理成左值

std::vector<int> createVector() {std::vector<int> v = {1, 2, 3};return v;
}
std::vector<int> v2 = std::move(createVector());

這里使用 move 的原因,由于不確定編譯器是否會優化,此時,即使返回值在理論上是右值,仍然可能會調用拷貝構造函數進行復制,因此需要使用 move 告訴編譯器需要使用,移動語義來傳遞對象,而不是依賴可能會發生也可能不會發生的返回值優化,函數返回值雖然在語義上是右值,但在語法形式上可以被當作左值處理

移動拷貝對于效率的提升是針對于自定義類型的深拷貝的類,因為只有深拷貝的類才有移動函數

對于內置類型和淺拷貝自定義類型,沒有移動函數

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

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

相關文章

小黑享受思考心流: 73. 矩陣置零

小黑代碼 class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""items []m len(matrix)n len(matrix[0])for i in range(m):for j in range(n):if not m…

精益數據分析(19/126):走出數據誤區,擁抱創業愿景

精益數據分析&#xff08;19/126&#xff09;&#xff1a;走出數據誤區&#xff0c;擁抱創業愿景 在創業與數據分析的探索之旅中&#xff0c;我們都渴望獲取更多知識&#xff0c;少走彎路。今天&#xff0c;我依然帶著和大家共同進步的想法&#xff0c;深入解讀《精益數據分析…

循環神經網絡RNN---LSTM

一、 RNN介紹 循環神經網絡&#xff08;Recurrent Neural Network&#xff0c;簡稱 RNN&#xff09;是一種專門用于處理序列數據的神經網絡&#xff0c;在自然語言處理、語音識別、時間序列預測等領域有廣泛應用。 傳統神經網絡 無法訓練出具有順序的數據。模型搭建時沒有考…

優考試V4.20機構版【附百度網盤鏈接】

優考試局域網考試系統具有強大的統計分析功能。優考試通過對考試數據進行統計分析&#xff0c;諸如考試分數分布&#xff0c;考試用時分布&#xff0c;錯排行等&#xff0c;讓你從整體上了解你的學員&#xff08;員工&#xff09;狀態&#xff0c; 同時你也可以對學員&#xff…

【Amazing晶焱科技高速 CAN Bus 傳輸與 TVS/ESD/EOS 保護,將是車用電子的生死關鍵無標題】

臺北國際車用電子展是亞洲地區重量級的車用電子科技盛會&#xff0c;聚焦于 ADAS、電動車動力系統、智慧座艙、人機界面、車聯網等領域。各大車廠與 Tier 1 供應鏈無不摩拳擦掌&#xff0c;推出最新技術與創新解決方案。 而今年&#xff0c;“智慧座艙” 無疑將成為全場焦點&am…

面試:結構體默認是對齊的嘛?如何禁止對齊?

是的。 結構體默認是對齊的?。結構體對齊是為了優化內存訪問速度和減少CPU訪問內存時的延遲。結構體對齊的規則如下&#xff1a; 某數據類型的變量存放的地址需要按有效對齊字節剩下的字節數可以被該數據類型所占字節數整除&#xff0c;char可以放在任意位置&#xff0c;int存…

如何優雅地解決AI生成內容粘貼到Word排版混亂的問題?

隨著AI工具的廣泛應用&#xff0c;越來越多人開始使用AI輔助撰寫論文、報告或博客。然而&#xff0c;當我們直接將AI生成的文本復制到Word文檔中時&#xff0c;常常會遇到排版混亂、格式異常的問題。這是因為大部分AI輸出時默認使用了Markdown格式&#xff0c;而Word對Markdown…

Golang | HashMap實現原理

HashMap是一種基于哈希表實現的鍵值對存儲結構&#xff0c;它通過哈希函數將鍵映射到數組的索引位置&#xff0c;支持高效的插入、查找和刪除操作。其核心原理如下&#xff1a; 哈希函數&#xff1a;將鍵轉換為數組索引。理想情況下&#xff0c;不同鍵應映射到不同索引&#xf…

vue3學習之防抖和節流

? 在前端開發中&#xff0c;我們經常會遇到這樣的情況&#xff1a;某些事件&#xff08;如滾動、輸入、點擊等&#xff09;會頻繁觸發&#xff0c;如果不加以控制&#xff0c;可能會導致性能問題。Vue3 中的防抖&#xff08;Debounce&#xff09;和節流&#xff08;Throttle&a…

4.2.2 MySQL索引原理以及SQL優化

文章目錄 4.2.2 MySQL索引原理以及SQL優化1. 索引與約束1. 索引是什么2. 索引的目的3. 幾種索引4. 約束1.外鍵2. 約束 vs 索引的區別 5. 索引實現1. 索引存儲2. 頁3. B樹4. B樹層高問題5. 自增id6. 聚集索引7. 輔助索引 8. innnodb體系結構1. buffer pool2. change buffer 9. 最…

【學習筆記】文件包含漏洞--本地遠程包含、偽協議、加密編碼

一、文件包含漏洞 和SQL等攻擊方式一樣&#xff0c;文件包含漏洞也是一種注入型漏洞&#xff0c;其本質就是輸入一段用戶能夠控制的腳本或者代碼&#xff0c;并讓服務端執行。 什么叫包含呢&#xff1f;以PHP為例&#xff0c;我們常常把可重復使用的函數寫入到單個文件中&…

藍橋杯 2021年模擬賽 掃雷問題

題目&#xff1a; 在一個 n 行 m 列的方格圖上有一些位置有地雷&#xff0c;另外一些位置為空。 請為每個空位置標一個整數&#xff0c;表示周圍八個相鄰的方格中有多少個地雷。 輸入描述 輸入的第一行包含兩個整數 n,m。 第 22行到第n1 行每行包含 m 個整數&#xff0c;相…

寫windows服務日志-.net4.5.2-定時修改數據庫中某些參數

環境&#xff1a; windows 11 Visual Studio 2015 .net 4.5.2 SQL Server 目的&#xff1a; 定時修改數據庫中某些參數的值 定時修改24小時內&#xff0c;SQL數據庫中&#xff0c;表JD_Reports 內&#xff0c;如果部門是‘體檢科&#xff0c;設置打印類型為 1 可以打印。步驟&a…

madvise MADV_FREE對文件頁統計的影響及原理

一、背景 madvise系統調用是一個與性能優化強相關的一個系統調用。madvise系統調用包括使用madvise函數&#xff0c;也包含使用posix_fadvise函數。如我們可以使用posix_fadvise傳入POSIX_FADV_DONTNEED來清除文件頁的page cache以減少內存壓力。 這篇博客里&#xff0c;我們…

于鍵值(KV)的表

基于鍵值&#xff08;KV&#xff09;的表 將行編碼為鍵值&#xff08;KVs&#xff09; 索引查詢&#xff1a;點查詢和范圍查詢 在關系型數據庫中&#xff0c;數據被建模為由行和列組成的二維表。用戶通過SQL表達他們的意圖&#xff0c;而數據庫則神奇地提供結果。不那么神奇的…

2025年邵陽市工程技術研究中心申報流程、條件、獎補

一、邵陽市工程技術研究中心申報條件 &#xff08;一&#xff09;工程技術研究中心主要依托科技型企業組建&#xff0c;依托單位應具有以下條件&#xff1a; 1.?具有較強技術創新意識的領導班子和技術水平高、工程化實踐經驗豐富的工程技術研發隊伍&#xff0c;其中固定人員…

Python+AI提示詞出租車出行軌跡預測:梯度提升GBR、KNN、LR回歸、隨機森林融合及貝葉斯概率異常檢測研究

原文鏈接&#xff1a;tecdat.cn/?p41693 在當今數字化浪潮席卷全球的時代&#xff0c;城市交通領域的海量數據如同蘊藏著無限價值的寶藏等待挖掘。作為數據科學家&#xff0c;我們肩負著從復雜數據中提取關鍵信息、構建有效模型以助力決策的使命&#xff08;點擊文末“閱讀原文…

系統重裝——聯想sharkbay主板電腦

上周給一臺老電腦重裝系統系統&#xff0c;型號是lenovo sharkbay主板的電腦&#xff0c;趁著最近固態便宜&#xff0c;入手了兩塊長城的固態&#xff0c;裝上以后插上啟動U盤&#xff0c;死活進不去boot系統。提示 bootmgr 缺失&#xff0c;上網查了許久&#xff0c;終于解決了…

python連接Elasticsearch并完成增刪改查

python庫提供了elasticsearch模塊,可以通過以下命令進行快速安裝,但是有個細節需要注意一下,安裝的模塊版本要跟es軟件版本一致,此處舉例:7.8.1 pip install elasticsearch==7.8.1 首先連接elasticsearch,以下是免密示例 from elasticsearch import Elasticsearch# El…

PDF嵌入圖片

所需依賴 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>9.0.0</version><type>pom</type> </dependency>源碼 /*** PDF工具*/ public class PdfUtils {/*** 嵌入圖…