<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我常用的10個C++新特性

          共 13610字,需瀏覽 28分鐘

           ·

          2021-08-28 14:27


          之前有讀者反饋?zhàn)屛铱偨Y(jié)一下C++常用的新特性,有讀者要求了,那我必須得總結(jié)一波!


          關(guān)于C++新特性我之前就寫過幾篇文章:

          《C++11新特性》點(diǎn)擊閱讀
          《C++14新特性》點(diǎn)擊閱讀
          《C++17新特性》點(diǎn)擊閱讀
          《C++20新特性》點(diǎn)擊閱讀


          之前文章介紹的是C++11之后所有的新特性,這篇文章我會總結(jié)一些常用的新特性,這些新特性可以說是必須掌握的!我也不太清楚其他人常用的新特性有啥,這里僅分享下我和身邊朋友們常用的新特性!


          下面是正文:

          auto類型推導(dǎo)

          auto可以讓編譯器在編譯器就推導(dǎo)出變量的類型,看代碼:

          auto a = 10; // 10是int型,可以自動推導(dǎo)出a是int
          int i = 10;auto b = i; // b是int型
          auto d = 2.0; // d是double型auto f = []() { // f是啥類型?直接用auto就行 return std::string("d");}


          利用auto可以通過=右邊的類型推導(dǎo)出變量的類型。


          什么時候使用auto呢?簡單類型其實(shí)沒必要使用auto,然而某些復(fù)雜類型就有必要使用auto,比如lambda表達(dá)式的類型,async函數(shù)的類型等,例如:

          auto func = [&] {        cout << "xxx";}; // 對于func你難道不使用auto嗎,反正我是不關(guān)心lambda表達(dá)式究竟是什么類型。auto asyncfunc = std::async(std::launch::async, func);


          智能指針

          C++11新特性中主要有兩種智能指針std::shared_ptr和std::unique_ptr。

          那什么時候使用std::shared_ptr,什么時候使用std::unique_ptr呢?

          • 當(dāng)所有權(quán)不明晰的情況,有可能多個對象共同管理同一塊內(nèi)存時,要使用std::shared_ptr;

          • 而std::unique_ptr強(qiáng)調(diào)的是獨(dú)占,同一時刻只能有一個對象占用這塊內(nèi)存,不支持多個對象共同管理同一塊內(nèi)存。


          兩類智能指針使用方式類似,拿std::unique_ptr舉例:

          using namespace std;
          struct A {   ~A() {       cout << "A delete" << endl;   }   void Print() {       cout << "A" << endl;   }};

          int main() {   auto ptr = std::unique_ptr<A>(new A);   auto tptr = std::make_unique<A>(); // error, c++11還不行,需要c++14   std::unique_ptr<A> tem = ptr; // error, unique_ptr不允許移動,編譯失敗   ptr->Print();   return 0;}


          std::lock相關(guān)

          C++11提供了兩種鎖封裝,通過RAII方式可動態(tài)的釋放鎖資源,防止編碼失誤導(dǎo)致始終持有鎖。


          這兩種封裝是std::lock_guard和std::unique_lock,使用方式類似,看下面的代碼:


          #include <iostream>#include <mutex>#include <thread>#include <chrono>
          using namespace std;std::mutex mutex_;
          int main() {   auto func1 = [](int k) {       // std::lock_guard<std::mutex> lock(mutex_);       std::unique_lock<std::mutex> lock(mutex_);       for (int i = 0; i < k; ++i) {           cout << i << " ";      }       cout << endl;  };   std::thread threads[5];   for (int i = 0; i < 5; ++i) {       threads[i] = std::thread(func1, 200);  }   for (auto& th : threads) {       th.join();  }   return 0;}


          普通情況下建議使用std::lock_guard,因?yàn)閟td::lock_guard更加輕量級,但如果用在條件變量的wait中環(huán)境中,必須使用std::unique_lock。


          條件變量

          條件變量是C++11引入的一種同步機(jī)制,它可以阻塞一個線程或多個線程,直到有線程通知或者超時才會喚醒正在阻塞的線程,條件變量需要和鎖配合使用,這里的鎖就是上面介紹的std::unique_lock。


          這里使用條件變量實(shí)現(xiàn)一個CountDownLatch:

          class CountDownLatch {   public:    explicit CountDownLatch(uint32_t count) : count_(count);
              void CountDown() {        std::unique_lock<std::mutex> lock(mutex_);        --count_;        if (count_ == 0) {            cv_.notify_all();        }    }
              void Await(uint32_t time_ms = 0) {        std::unique_lock<std::mutex> lock(mutex_);        while (count_ > 0) {            if (time_ms > 0) {                cv_.wait_for(lock, std::chrono::milliseconds(time_ms));            } else {                cv_.wait(lock);            }        }    }
              uint32_t GetCount() const {        std::unique_lock<std::mutex> lock(mutex_);      return count_;    }
             private:    std::condition_variable cv_;    mutable std::mutex mutex_;    uint32_t count_ = 0;};


          條件變量還有幾個坑,可以看這篇文章:《使用條件變量的坑你知道嗎


          原子操作

          C++11提供了原子類型std::atomic,用于原子操作,使用這種方式既可以保證線程安全,也不需要使用鎖來進(jìn)行臨界區(qū)保護(hù),對一些普通變量來說尤其方便,看代碼:

          std::atomic<int> atomicInt;atomicInt++;atomicInt--;atomicInt.store(2);int value = atomicInt.load();


          多線程

          什么是多線程這里就不過多介紹,新特性關(guān)于多線程最主要的就是std::thread的使用,它的使用也很簡單,看代碼:

          #include <iostream>#include <thread>
          using namespace std;
          int main() {   auto func = []() {       for (int i = 0; i < 10; ++i) {           cout << i << " ";      }       cout << endl;  };   std::thread t(func);   if (t.joinable()) {       t.detach();  }   auto func1 = [](int k) {       for (int i = 0; i < k; ++i) {           cout << i << " ";      }       cout << endl;  };   std::thread tt(func1, 20);   if (tt.joinable()) { // 檢查線程可否被join       tt.join();  }   return 0;}


          這里記住,std::thread在其對象生命周期結(jié)束時必須要調(diào)用join()或者detach(),否則程序會terminate(),這個問題在C++20中的std::jthread得到解決,但是C++20現(xiàn)在多數(shù)編譯器還沒有完全支持所有特性,先暫時了解下即可,項(xiàng)目中沒必要著急使用。


          左值右值移動語義相關(guān)

          大家可能都聽說過左值右值,但可能會有部分讀者還沒有搞清楚這些概念。這里解惑下:


          關(guān)于左值和右值,有兩種方式理解:


          概念1

          左值:可以放到等號左邊的東西叫左值。

          右值:不可以放到等號左邊的東西就叫右值。


          概念2

          左值:可以取地址并且有名字的東西就是左值。

          右值:不能取地址的沒有名字的東西就是右值。


          舉例來說:

          int a = b + cint d = 4; // d是左值,4作為普通字面量,是右值


          a是左值,有變量名,可以取地址,也可以放到等號左邊, 表達(dá)式b+c的返回值是右值,沒有名字且不能取地址,&(b+c)不能通過編譯,而且也不能放到等號左邊。


          左值一般有:

          • 函數(shù)名和變量名

          • 返回左值引用的函數(shù)調(diào)用

          • 前置自增自減表達(dá)式++i、--i

          • 由賦值表達(dá)式或賦值運(yùn)算符連接的表達(dá)式(a=b, a += b等)

          • 解引用表達(dá)式*p

          • 字符串字面值"abcd"


          介紹右值前需要先介紹兩個概念:純右值和將亡值。


          運(yùn)算表達(dá)式產(chǎn)生的臨時變量、不和對象關(guān)聯(lián)的原始字面量、非引用返回的臨時變量、lambda表達(dá)式等都是純右值。例如:

          • 除字符串字面值外的字面值

          • 返回非引用類型的函數(shù)調(diào)用

          • 后置自增自減表達(dá)式i++、i--

          • 算術(shù)表達(dá)式(a+b, a*b, a&&b, a==b等)

          • 取地址表達(dá)式等(&a)


          而將亡值是指C++11新增的和右值引用相關(guān)的表達(dá)式,通常指將要被移動的對象、T&&函數(shù)的返回值、std::move函數(shù)的返回值、轉(zhuǎn)換為T&&類型轉(zhuǎn)換函數(shù)的返回值,將亡值可以理解為即將要銷毀的值,通過“盜取”其它變量內(nèi)存空間方式獲取的值,在確保其它變量不再被使用或者即將被銷毀時,可以避免內(nèi)存空間的釋放和分配,延長變量值的生命周期,常用來完成移動構(gòu)造或者移動賦值的特殊任務(wù)。例如:

          class A {    xxx;};A a;auto c = std::move(a); // c是將亡值auto d = static_cast<A&&>(a); // d是將亡值

          這塊的概念太多了,涉及很多知識點(diǎn),這里不太展開介紹了,具體可以看這篇文章:《左值引用、右值引用、移動語義、完美轉(zhuǎn)發(fā),你知道的不知道的都在這里



          std::function和lambda表達(dá)式

          這兩個可以說是我最常用的特性,使用它們會讓函數(shù)的調(diào)用相當(dāng)方便。使用std::function可以完全替代以前那種繁瑣的函數(shù)指針形式。


          還可以結(jié)合std::bind一起使用,直接看一段示例代碼:

          std::function<void(int)> f; // 這里表示function的對象f的參數(shù)是int,返回值是void#include <functional>#include <iostream>
          struct Foo {   Foo(int num) : num_(num) {}   void print_add(int i) const std::cout << num_ + i << '\n'; }   int num_;};
          void print_num(int i) std::cout << i << '\n'; }
          struct PrintNum {   void operator()(int i) const std::cout << i << '\n'; }};
          int main() {   // 存儲自由函數(shù)   std::function<void(int)> f_display = print_num;   f_display(-9);
             // 存儲 lambda   std::function<void()> f_display_42 = []() { print_num(42); };   f_display_42();
             // 存儲到 std::bind 調(diào)用的結(jié)果   std::function<void()> f_display_31337 = std::bind(print_num, 31337);   f_display_31337();
             // 存儲到成員函數(shù)的調(diào)用   std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;   const Foo foo(314159);   f_add_display(foo, 1);   f_add_display(3141591);
             // 存儲到數(shù)據(jù)成員訪問器的調(diào)用   std::function<int(Foo const&)> f_num = &Foo::num_;   std::cout << "num_: " << f_num(foo) << '\n';
             // 存儲到成員函數(shù)及對象的調(diào)用   using std::placeholders::_1;   std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);   f_add_display2(2);
             // 存儲到成員函數(shù)和對象指針的調(diào)用   std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);   f_add_display3(3);
             // 存儲到函數(shù)對象的調(diào)用   std::function<void(int)> f_display_obj = PrintNum();   f_display_obj(18);}


          從上面可以看到std::function的使用方法,當(dāng)給std::function填入合適的參數(shù)表和返回值后,它就變成了可以容納所有這一類調(diào)用方式的函數(shù)封裝器。std::function還可以用作回調(diào)函數(shù),或者在C++里如果需要使用回調(diào)那就一定要使用std::function,特別方便,這方面的使用方式大家可以讀下我之前的文章《搞定c++11新特性std::function和lambda表達(dá)式


          lambda表達(dá)式可以說是C++11引入的最重要的特性之一,它定義了一個匿名函數(shù),可以捕獲一定范圍的變量在函數(shù)內(nèi)部使用,一般有如下語法形式:

          auto func = [capture] (params) opt -> ret { func_body; };

          其中func是可以當(dāng)作lambda表達(dá)式的名字,作為一個函數(shù)使用,capture是捕獲列表,params是參數(shù)表,opt是函數(shù)選項(xiàng)(mutable之類), ret是返回值類型,func_body是函數(shù)體。


          看下面這段使用lambda表達(dá)式的示例吧:

          auto func1 = [](int a) -> int { return a + 1; }; auto func2 = [](int a) { return a + 2; }; cout << func1(1) << " " << func2(2) << endl;

          std::function和std::bind使得我們平時編程過程中封裝函數(shù)更加的方便,而lambda表達(dá)式將這種方便發(fā)揮到了極致,可以在需要的時間就地定義匿名函數(shù),不再需要定義類或者函數(shù)等,在自定義STL規(guī)則時候也非常方便,讓代碼更簡潔,更靈活,提高開發(fā)效率。

          std::file_system

          C++17正式將file_system納入標(biāo)準(zhǔn)中,提供了關(guān)于文件的大多數(shù)功能,基本上應(yīng)有盡有,這里簡單舉幾個例子:

          namespace fs = std::filesystem;fs::create_directory(dir_path);fs::copy_file(src, dst, fs::copy_options::skip_existing);fs::exists(filename);fs::current_path(err_code);

          file_system之前,想拷貝個文件、獲取文件信息等都需要使用好多C語言API搭配使用才能完成需求,而有了file_system,一切都變得相當(dāng)簡單。file_system是C++17才引入的新功能,但其實(shí)在C++14中就可以使用了,只是file_system在std::experimental空間下。



          std::chrono

          chrono很強(qiáng)大,也是我常用的功能,平時的打印函數(shù)耗時,休眠某段時間等,我都是使用chrono。


          在C++11中引入了duration、time_point和clocks,在C++20中還進(jìn)一步支持了日期和時區(qū)。這里簡要介紹下C++11中的這幾個新特性。


          duration

          std::chrono::duration表示一段時間,常見的單位有s、ms等,示例代碼:

          // 拿休眠一段時間舉例,這里表示休眠100msstd::this_thread::sleep_for(std::chrono::milliseconds(100));

          sleep_for里面其實(shí)就是std::chrono::duration,表示一段時間,實(shí)際是這樣:

          typedef duration<int64_t, milli> milliseconds;typedef duration<int64_t> seconds;


          duration具體模板如下:

          template <class Rep, class Period = ratio<1> > class duration;


          Rep表示一種數(shù)值類型,用來表示Period的數(shù)量,比如int、float、double,Period是ratio類型,用來表示【用秒表示的時間單位】比如second,常用的duration已經(jīng)定義好了,在std::chrono::duration下:


          • ratio<3600, 1>:hours
          • ratio<60, 1>:minutes
          • ratio<1, 1>:seconds
          • ratio<1, 1000>:microseconds
          • ratio<1, 1000000>:microseconds
          • ratio<1, 1000000000>:nanosecons


          ratio的具體模板如下:

          template <intmax_t N, intmax_t D = 1> class ratio;


          N代表分子,D代表分母,所以ratio表示一個分?jǐn)?shù),我們可以自定義Period,比如ratio<2, 1>表示單位時間是2秒。


          time_point

          表示一個具體時間點(diǎn),如2020年5月10日10點(diǎn)10分10秒,拿獲取當(dāng)前時間舉例:

          std::chrono::time_point<std::chrono::high_resolution_clock> Now() {   return std::chrono::high_resolution_clock::now();}// std::chrono::high_resolution_clock為高精度時鐘,下面會提到


          clocks

          時鐘,chrono里面提供了三種時鐘:

          • steady_clock

          • system_clock

          • high_resolution_clock


          steady_clock

          穩(wěn)定的時間間隔,表示相對時間,相對于系統(tǒng)開機(jī)啟動的時間,無論系統(tǒng)時間如何被更改,后一次調(diào)用now()肯定比前一次調(diào)用now()的數(shù)值大,可用于計時。


          system_clock

          表示當(dāng)前的系統(tǒng)時鐘,可以用于獲取當(dāng)前時間:

          int main() {   using std::chrono::system_clock;   system_clock::time_point today = system_clock::now();   std::time_t tt = system_clock::to_time_t(today);   std::cout << "today is: " << ctime(&tt);   return 0;}// today is: Sun May 10 09:48:36 2020


          high_resolution_clock

          high_resolution_clock表示系統(tǒng)可用的最高精度的時鐘,實(shí)際上就是system_clock或者steady_clock其中一種的定義,官方?jīng)]有說明具體是哪個,不同系統(tǒng)可能不一樣,我之前看gcc chrono源碼中high_resolution_clock是steady_clock的typedef。



          瀏覽 52
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩视频二区在线 | 99精品偷拍 | 美国一级在线 | 国内精品人妻无码久久久影院蜜桃 | 人人爱三级片 |