C++編程經(jīng)驗(yàn)(12):C++11新特性

      網(wǎng)友投稿 876 2025-04-02

      沒(méi)有系統(tǒng)學(xué)過(guò),所以這篇寫的基本都是我接觸過(guò)的,接觸過(guò)多少就整理多少吧。


      有些特性也不知道是不是新的,反正都是我新接觸的,用的還挺順手。

      語(yǔ)法層面

      區(qū)間迭代range for

      用過(guò)一次我就很喜歡這個(gè)特性了,寫起來(lái)是方便了不少。

      for(int i:vec){ cout<

      nullptr

      這是一個(gè)空指針類的對(duì)象。

      我們以前把指針置空都是:

      ptr = NULL;

      NULL是一個(gè)宏定義,數(shù)值為0。當(dāng)然不是說(shuō)用NULL有什么問(wèn)題,不過(guò)新的規(guī)范都出來(lái)了,就用新規(guī)也沒(méi)什么不好嘛。

      強(qiáng)制類型轉(zhuǎn)換(這個(gè)其實(shí)不是)

      static_cast:正常的類型轉(zhuǎn)換,static_cast 不能從表達(dá)式中去除 const 屬性 const_cast:用于且僅用于類型轉(zhuǎn)換掉表達(dá)式的 const 或 volatileness 屬性。 dynamic_cast:用于安全地沿著類的繼承關(guān)系向下進(jìn)行類型轉(zhuǎn)換。 reinterpret_cast:在函數(shù)指針類型之間進(jìn)行轉(zhuǎn)換,這個(gè)轉(zhuǎn)換符不是很受待見(jiàn) 其的轉(zhuǎn)換結(jié)果幾乎都是執(zhí)行期定義。 因此,使用reinterpret_casts 的代碼很難移植。

      示例:

      int a; double result = static_cast(a);

      class father { ... }; class son: public father { ... }; void update(son* psw); son sw; // sw 是一個(gè)非 const 對(duì)象。 const son& csw = sw; // csw 是 sw 的一個(gè)引用,它是一個(gè) const 對(duì)象 update(&csw); // 錯(cuò)誤!不能傳遞一個(gè) const son* 變量給一個(gè)處理 son*類型變量的函數(shù) update(const_cast(&csw)); // 正確,csw 的 const 被顯示地轉(zhuǎn)換掉 update((son*)&csw); // 同上,但用了一個(gè)更難識(shí)別的 C 風(fēng)格的類型轉(zhuǎn)換 father *pw = new son; update(pw); // 錯(cuò)誤!pw 的類型是 father*,但是 update 函數(shù)處理的是 son*類型 update(const_cast(pw));// 錯(cuò)誤!const_cast 僅能被用在影響 constness or volatileness 的地方上。, // 不能用在向繼承子類進(jìn)行類型轉(zhuǎn)換。

      father* pw; ... update(dynamic_cast(pw)); // 正確,傳遞給 update 函數(shù)一個(gè)指針是指向變量類型為 son的 pw 的指針 void updateViaRef(son& rsw); updateViaRef(dynamic_cast(*pw)); //正確。 傳遞給 updateViaRef 函數(shù) SpecialWidget pw 指針

      智能指針

      智能指針是存儲(chǔ)指向動(dòng)態(tài)分配(堆)對(duì)象指針的類。除了能夠在適當(dāng)?shù)臅r(shí)間自動(dòng)刪除指向的對(duì)象外,他們的工作機(jī)制很像C++的內(nèi)置指針。

      在使用對(duì)象的時(shí)候,使用強(qiáng)智能指針;在引用對(duì)象的時(shí)候,使用弱智能指針。

      詳情轉(zhuǎn):C++編程經(jīng)驗(yàn)(9):智能指針 – 裸指針管得了的我要管,裸指針管不了的我更要管!

      function函數(shù)對(duì)象、bind綁定器、placeholders占位符

      這三個(gè)還是要合在一起講的了。

      C++編程經(jīng)驗(yàn)(11):std::function 和 bind綁定器,雖然在這一篇里面專門講過(guò)了,但是感覺(jué)有點(diǎn)抽象,重新捋一下,不然我也不長(zhǎng)記性吶。

      using MsgHandler = std::function; 理解為: typename void(const TcpConnectionPtr &conn,json &js,Timestamp time) MsgHandler ;

      MsgHandler 是一個(gè)自定義數(shù)據(jù)類型,函數(shù)指針。

      既然是一個(gè)數(shù)據(jù)類型,就可以被塞到容器里面:

      unordered_map _msgHanderMap;

      函數(shù)指針有什么用,它就有什么用,可以用來(lái)推遲函數(shù)的聲明。

      綁定器是干嘛的呢?將參數(shù)綁定到函數(shù)指針上的。

      以前的綁定器只能綁定一個(gè)參數(shù),所以我們看到的很多古老的需要函數(shù)指針做傳參的函數(shù)都只有一個(gè)參數(shù)傳遞,但是有了新的綁定器就不一樣了。

      _msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)}); //將三個(gè)參數(shù)綁定到login函數(shù)上,由于是在類內(nèi),所以帶上一個(gè)this。 std::bind(&ChatService::login,this,_1,_2,_3) //這三個(gè)參數(shù)使用占位符事先申明

      綁定好了,現(xiàn)在要調(diào)用這個(gè)函數(shù)就需要在調(diào)用的時(shí)候傳參,那被綁定的函數(shù)要如何取參數(shù),這就取決于占位符的聲明了。

      std::placeholders決定==函數(shù)占用位置取用輸入?yún)?shù)的第幾個(gè)參數(shù)==。

      那么現(xiàn)在一條脈絡(luò)就很清楚了。

      要使用函數(shù)指針,使用function進(jìn)行函數(shù)指針模板的聲明與調(diào)用;

      實(shí)例化function模板所用的函數(shù)可能有不下于1個(gè)的參數(shù),舊的綁定器已經(jīng)不行了,用新的綁定器來(lái)吧;

      而函數(shù)指針需要從調(diào)用函數(shù)指針的函數(shù)那里去獲得傳入?yún)?shù),這個(gè)參數(shù)的位置排序的確定就需要靠占位符來(lái)指定了,或許可以稱之為導(dǎo)航符吧。

      lambda表達(dá)式

      簡(jiǎn)單來(lái)說(shuō),Lambda函數(shù)也就是一個(gè)函數(shù),它的語(yǔ)法定義如下:

      [capture](parameters) mutable ->return-type{statement}

      1.[capture]:捕捉列表。捕捉列表總是出現(xiàn)在Lambda函數(shù)的開(kāi)始處。實(shí)際上,[]是Lambda引出符。編譯器根據(jù)該引出符判斷接下來(lái)的代碼是否是Lambda函數(shù)。捕捉列表能夠捕捉上下文中的變量以供Lambda函數(shù)使用;

      2.(parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致。如果不需要參數(shù)傳遞,則可以連同括號(hào)“()”一起省略;

      3.mutable:mutable修飾符。默認(rèn)情況下,Lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。在使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空);

      4.->return-type:返回類型。用追蹤返回類型形式聲明函數(shù)的返回類型。我們可以在不需要返回值的時(shí)候也可以連同符號(hào)”->”一起省略。此外,在返回類型明確的情況下,也可以省略該部分,讓編譯器對(duì)返回類型進(jìn)行推導(dǎo);

      5.{statement}:函數(shù)體。內(nèi)容與普通函數(shù)一樣,不過(guò)除了可以使用參數(shù)之外,還可以使用所有捕獲的變量。

      與普通函數(shù)最大的區(qū)別是,除了可以使用參數(shù)以外,Lambda函數(shù)還可以通過(guò)捕獲列表訪問(wèn)一些上下文中的數(shù)據(jù)。具體地,捕捉列表描述了上下文中哪些數(shù)據(jù)可以被Lambda使用,以及使用方式(以值傳遞的方式或引用傳遞的方式)。語(yǔ)法上,在“[]”包括起來(lái)的是捕捉列表,捕捉列表由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分隔。捕捉列表有以下幾種形式:

      1.[var]表示值傳遞方式捕捉變量var; 2.[=]表示值傳遞方式捕捉所有父作用域的變量(包括this); 3.[&var]表示引用傳遞捕捉變量var; 4.[&]表示引用傳遞方式捕捉所有父作用域的變量(包括this); 5.[this]表示值傳遞方式捕捉當(dāng)前的this指針。 6.[]沒(méi)有任何函數(shù)對(duì)象參數(shù)。 7.&a。將 a 按引用進(jìn)行傳遞。 8.a,&b。將 a 按值傳遞,b 按引用進(jìn)行傳遞。 9.=,&a,&b。除 a 和 b 按引用進(jìn)行傳遞外,其他參數(shù)都按值進(jìn)行傳遞。 10.&,a,b。除 a 和 b 按值進(jìn)行傳遞外,其他參數(shù)都按引用進(jìn)行傳遞。

      move

      對(duì)于move了解不多。

      C++11為了解決這個(gè)問(wèn)題,提供了std::move()方法來(lái)將左值轉(zhuǎn)換為右值,從而方便應(yīng)用移動(dòng)語(yǔ)義。move是將對(duì)象的狀態(tài)或者所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象,只是轉(zhuǎn)義,沒(méi)有內(nèi)存拷貝。

      類相關(guān)

      explicit類型轉(zhuǎn)換運(yùn)算符

      防止類構(gòu)造發(fā)生默認(rèn)類型轉(zhuǎn)換

      對(duì)這個(gè)關(guān)鍵字我現(xiàn)在持懷疑態(tài)度了,是我的VS壞了,還是我的眼睛瞎了呢?

      下面三個(gè)測(cè)試案例結(jié)果都是一樣的。

      #include #include using namespace std; //發(fā)生了轉(zhuǎn)換 //class A { //public: // explicit A(int i,int j) { // cout << i << endl; // } // //}; // // //int main() { // A a('a', 20); //} //依舊發(fā)生了轉(zhuǎn)換,有什么區(qū)別嗎? //class A { //public: // explicit A(int i) { // cout << i << endl; // } // //}; // // //int main() { // A a('a'); //} class A { public: A(int i) { cout << i << endl; } }; int main() { A a('a'); }

      =default和=delete

      如果實(shí)現(xiàn)了默認(rèn)的構(gòu)造函數(shù),編譯器則不會(huì)自動(dòng)生成默認(rèn)版本;可以通過(guò)使用關(guān)鍵字 default 來(lái)控制默認(rèn)構(gòu)造函數(shù)的生成,顯示的指示編譯器生成該函數(shù)的默認(rèn)版本;

      如果不想有某些默認(rèn)生成的函數(shù),就設(shè)置一個(gè) =delete。

      如果給類手動(dòng)寫了帶參構(gòu)造,那也是無(wú)法顯式使用無(wú)參構(gòu)造函數(shù)了。

      如果沒(méi)有了默認(rèn)構(gòu)造,子類就不能不傳參給父類進(jìn)行構(gòu)造了。

      override、final

      final關(guān)鍵字的作用是使派生類不可覆蓋它所修飾的虛函數(shù)。

      override關(guān)鍵字的作用是使派生類被制定的函數(shù)必須是覆蓋它所修飾的虛函數(shù)。

      using

      現(xiàn)在不僅僅可以用它來(lái)引用名空間了,不過(guò)現(xiàn)在我也不怎么用這個(gè)來(lái)引用名空間了,都是用域作用符::。

      現(xiàn)在用它都是用來(lái)替代以前的typedef了,而且一般是和下面的function函數(shù)對(duì)象結(jié)合在一起使用,最近在整muduo,這些接觸到的會(huì)比較多。

      volatile

      如上圖所示,所有線程的共享變量都存儲(chǔ)在主內(nèi)存中,每一個(gè)線程都有一個(gè)獨(dú)有的工作內(nèi)存,每個(gè)線程不直接操作在主內(nèi)存中的變量,而是將主內(nèi)存上變量的副本放進(jìn)自己的工作內(nèi)存中,只操作工作內(nèi)存中的數(shù)據(jù)。當(dāng)修改完畢后,再把修改后的結(jié)果放回到主內(nèi)存中。每個(gè)線程都只操作自己工作內(nèi)存中的變量,無(wú)法直接訪問(wèn)對(duì)方工作內(nèi)存中的變量,線程間變量值的傳遞需要通過(guò)主內(nèi)存來(lái)完成。

      如果對(duì)變量 i 加上 volatile 關(guān)鍵字修飾的話,它可以保證當(dāng) A 線程對(duì)變量 i 值做了變動(dòng)之后,會(huì)立即刷回到主內(nèi)存中,而其它線程讀取到該變量的值也作廢,強(qiáng)迫重新從主內(nèi)存中讀取該變量的值,這樣在任何時(shí)刻,AB線程總是會(huì)看到變量 i 的同一個(gè)值。

      它不是原子操作的。

      線程

      Thread

      std::thread無(wú)疑是一個(gè)重磅福利。

      std::thread 在 頭文件中聲明,因此使用 std::thread 時(shí)需要包含 頭文件。

      默認(rèn)構(gòu)造函數(shù) thread() noexcept; 初始化構(gòu)造函數(shù) template explicit thread(Fn&& fn, Args&&... args); 拷貝構(gòu)造函數(shù) [deleted] thread(const thread&) = delete; Move 構(gòu)造函數(shù) thread(thread&& x) noexcept; Move 賦值操作 thread& operator=(thread&& rhs) noexcept; 拷貝賦值操作 [deleted] thread& operator=(const thread&) = delete;

      默認(rèn)構(gòu)造函數(shù),創(chuàng)建一個(gè)空的 std::thread 執(zhí)行對(duì)象。

      初始化構(gòu)造函數(shù),創(chuàng)建一個(gè) std::thread 對(duì)象,該 std::thread 對(duì)象可被 joinable,新產(chǎn)生的線程會(huì)調(diào)用 fn 函數(shù),該函數(shù)的參數(shù)由 args 給出。

      拷貝構(gòu)造函數(shù)(被禁用),意味著 std::thread 對(duì)象不可拷貝構(gòu)造。

      Move 構(gòu)造函數(shù),,調(diào)用成功之后 x 不代表任何 std::thread 執(zhí)行對(duì)象。

      注意:可被 joinable 的 std::thread 對(duì)象必須在他們銷毀之前被主線程 join 或者將其設(shè)置為 detached.

      Move 賦值操作(1),如果當(dāng)前對(duì)象不可 joinable,需要傳遞一個(gè)右值引用(rhs)給 move 賦值操作;如果當(dāng)前對(duì)象可被 joinable,則會(huì)調(diào)用 terminate() 報(bào)錯(cuò)。

      拷貝賦值操作(2),被禁用,因此 std::thread 對(duì)象不可拷貝賦值。

      get_id: 獲取線程 ID,返回一個(gè)類型為 std::thread::id 的對(duì)象。

      joinable: 檢查線程是否可被 join。檢查當(dāng)前的線程對(duì)象是否表示了一個(gè)活動(dòng)的執(zhí)行線程,由默認(rèn)構(gòu)造函數(shù)創(chuàng)建的線程是不能被 join 的。另外,如果某個(gè)線程 已經(jīng)執(zhí)行完任務(wù),但是沒(méi)有被 join 的話,該線程依然會(huì)被認(rèn)為是一個(gè)活動(dòng)的執(zhí)行線程,因此也是可以被 join 的。

      detach: Detach 線程。 將當(dāng)前線程對(duì)象所代表的執(zhí)行實(shí)例與該線程對(duì)象分離,使得線程的執(zhí)行可以單獨(dú)進(jìn)行。一旦線程執(zhí)行完畢,它所分配的資源將會(huì)被釋放。

      swap: Swap 線程,交換兩個(gè)線程對(duì)象所代表的底層句柄

      thread 1 id: 1892 thread 2 id: 2584 after std::swap(t1, t2): thread 1 id: 2584 thread 2 id: 1892 after t1.swap(t2): thread 1 id: 1892 thread 2 id: 2584

      yield: 當(dāng)前線程放棄當(dāng)前時(shí)間片,操作系統(tǒng)調(diào)度另一線程繼續(xù)執(zhí)行。

      sleep_until: 線程休眠至某個(gè)指定的時(shí)刻(time point),該線程才被重新喚醒。

      sleep_for: 線程休眠某個(gè)指定的時(shí)間片(time span),該線程才被重新喚醒,不過(guò)由于線程調(diào)度等原因,實(shí)際休眠時(shí)間可能比 sleep_duration 所表示的時(shí)間片更長(zhǎng)。

      縮略muduo庫(kù)(5):Thread、EventThread、EventThreadPool

      對(duì)這份線程池我還是有自信的。

      鎖種

      創(chuàng)建lock_guard對(duì)象時(shí),它將嘗試獲取提供給它的互斥鎖的所有權(quán)。當(dāng)控制流離開(kāi)lock_guard對(duì)象的作用域時(shí),lock_guard析構(gòu)并釋放互斥量。

      它的特點(diǎn)如下:

      創(chuàng)建即加鎖,作用域結(jié)束自動(dòng)析構(gòu)并解鎖,無(wú)需手工解鎖 不能中途解鎖,必須等作用域結(jié)束才解鎖 不能復(fù)制

      簡(jiǎn)單地講,unique_lock 是 lock_guard 的升級(jí)加強(qiáng)版,它具有 lock_guard 的所有功能,同時(shí)又具有其他很多方法,使用起來(lái)更強(qiáng)靈活方便,能夠應(yīng)對(duì)更復(fù)雜的鎖定需要。

      特點(diǎn)如下:

      創(chuàng)建時(shí)可以不鎖定(通過(guò)指定第二個(gè)參數(shù)為std::defer_lock),而在需要時(shí)再鎖定 可以隨時(shí)加鎖解鎖 作用域規(guī)則同 lock_grard,析構(gòu)時(shí)自動(dòng)釋放鎖 不可復(fù)制,可移動(dòng) 條件變量需要該類型的鎖作為參數(shù)(此時(shí)必須使用unique_lock)

      示例:

      #include #include #include struct Box { explicit Box(int num) : num_things{num} {} int num_things; std::mutex m; }; void transfer(Box &from, Box &to, int num) { // don't actually take the locks yet std::unique_lock lock1(from.m, std::defer_lock); std::unique_lock lock2(to.m, std::defer_lock); // lock both unique_locks without deadlock std::lock(lock1, lock2); from.num_things -= num; to.num_things += num; // 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors } int main() { Box acc1(100); Box acc2(50); std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10); std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5); t1.join(); t2.join(); }

      條件變量。

      通知方:

      獲取 std::mutex, 通常是 std::lock_guard 修改共享變量(即使共享變量是原子變量,也需要在互斥對(duì)象內(nèi)進(jìn)行修改,以保證正確地將修改發(fā)布到等待線程) 在 condition_variable 上執(zhí)行 notify_one/notify_all 通知條件變量(該操作不需要鎖)

      等待方:

      獲取相同的 std::mutex, 使用 std::unique_lock 執(zhí)行 wait,wait_for或wait_until(該操作會(huì)自動(dòng)釋放鎖并阻塞) 接收到條件變量通知、超時(shí)或者發(fā)生虛假喚醒時(shí),線程被喚醒,并自動(dòng)獲取鎖。喚醒的線程負(fù)責(zé)檢查共享變量,如果是虛假喚醒,則應(yīng)繼續(xù)等待

      std :: condition_variable僅適用于 std::unique_lock

      對(duì)于只需要通知一次的情況,如初始化完成、登錄成功等,建議不要使用 condition_variable,使用std::future更好。不過(guò)這個(gè)我還沒(méi)有去了解。

      在有些場(chǎng)景里面,是需要對(duì)一些資源進(jìn)行鎖定的。但是有些資源實(shí)在是太小了,鎖定的粒度也太小了,不免顯得上鎖解鎖倒成了繁瑣。

      比方說(shuō):

      _mlock.lock(); count++; _mlock.unlock();

      CAS,是基于硬件層面的無(wú)鎖操作,由CPU來(lái)保證。

      #include #include #include #include //其中包含很多原子操作 #include using namespace std; volatile atomic_bool isReady = false; //volatile:防止共享變量被緩存,導(dǎo)致線程跑來(lái)跑去 volatile atomic_int mycount = 0; void task() { while (!isReady) { this_thread::yield(); //出讓時(shí)間片,等待下一次調(diào)用 } for (int i = 0; i < 100; i++) { mycount++; } } int main() { vector tvec; for (int i = 0; i < 10;i++) { tvec.push_back(thread(task)); } this_thread::sleep_for(chrono::seconds(3)); isReady = true; for (thread& t : tvec) { t.join(); } cout << mycount << endl; return 0; }

      容器相關(guān)

      C++編程經(jīng)驗(yàn)(12):C++11新特性

      unordered_XXX

      哈希表。

      容器的emplace成員

      emplace操作是C++11新特性,新引入的的三個(gè)成員emplace_front、emplace 和 emplace_back。這些操作構(gòu)造而不是拷貝元素到容器中,這些操作分別對(duì)應(yīng)push_front、insert 和push_back,允許我們將元素放在容器頭部、一個(gè)指定的位置和容器尾部。

      C++

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel2007表格如何制作圖表(excel2010如何制作圖表)
      下一篇:如何更改圖表中圖例的內(nèi)容?(怎樣更改表格里圖例的內(nèi)容)
      相關(guān)文章
      亚洲综合亚洲综合网成人| 亚洲欧洲日产韩国在线| 亚洲av之男人的天堂网站| 亚洲色大成网站www永久男同| 国产亚洲女在线线精品| 日本亚洲色大成网站www久久 | 亚洲中文字幕无码久久精品1| 在线观看亚洲电影| 亚洲国产欧美一区二区三区| 亚洲一区二区三区精品视频| 亚洲午夜成激人情在线影院| 久久久久亚洲AV无码专区首JN | 亚洲精品女同中文字幕| 亚洲春色在线观看| 久久精品国产亚洲av麻豆小说 | 国产精品亚洲а∨无码播放麻豆| 亚洲无人区码一二三码区别图片 | 国产亚洲日韩在线三区| 亚洲国产成人久久精品99 | 亚洲尹人九九大色香蕉网站| 久久久久久亚洲精品中文字幕| 亚洲日韩精品一区二区三区无码| 精品国产香蕉伊思人在线在线亚洲一区二区 | 狠狠色婷婷狠狠狠亚洲综合 | 亚洲人成在线免费观看| 亚洲五月丁香综合视频| va天堂va亚洲va影视中文字幕 | 2020久久精品亚洲热综合一本| 最新国产成人亚洲精品影院| 中文日韩亚洲欧美制服| 亚洲日韩一区精品射精| 亚洲精品久久久久无码AV片软件| 亚洲精品无码永久在线观看男男| 亚洲av无码专区国产不乱码| 亚洲AV无码一区二区三区鸳鸯影院| 亚洲国产专区一区| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 亚洲国产精品无码成人片久久| 亚洲日韩av无码| 午夜亚洲www湿好大| 亚洲av无码一区二区三区天堂古代 |