<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>

          提升 C++ 開發(fā)效率的幾個小技巧

          共 33906字,需瀏覽 68分鐘

           ·

          2021-08-10 21:56

          我們說的 Modern C++,一般指的是 C++11 及以后的標準,從 C++ 11 開始,Modern C++ 引入了大量的實用的特性,主要是兩大方面,學習的時候也可以從這兩大方面學習:

          1. 增強或者改善的語法特性;
          2. 新增的或者改善的 STL 庫。

          我們來看幾個具體的案例:

          案例 1:統(tǒng)一的類成員初始化語法與 std::initializer_list

          在 C++98/03 中,假設我們要初始化一個類數(shù)組類型的成員(例如常用的清零操作),我們需要這么寫:

           class A
           {
           public:
               A()
               {
                   //初始化arr
                   arr[0] = 0;
                   arr[1] = 0;
                   arr[2] = 0;
                   arr[3] = 0;
               }
           
           public:
               int arr[4];
           };

          假設數(shù)組 arr 較長,我們可以使用循環(huán)或者借助 memset 函數(shù)去初始化,代碼如下:

           class A
           {
           public:
               A()
               {
                   //使用循環(huán)初始化arr
                   for (int i = 0; i < 4; i++)
                       arr[i] = 0;
               }
           
           public:
               int arr[4];
           };
           
           class A
           {
           public:
               A()
               {
                   //使用memset初始化arr
                   memset(arr, 0, sizeof(arr));
               }
           
           public:
               int arr[4];
           };

          但是,我們知道,在 C++98/08 中我們可以直接通過賦值操作來初始化一個數(shù)組的:

           int arr[4] = { 0 };

          但是對于作為類的成員變量的數(shù)組元素,C++98/03 是不允許我們這么做的。

          到 C++11 中全部放開并統(tǒng)一了,在 C++11 中我們也可以使用這樣的語法是初始化數(shù)組:

           class A
           {
           public:
               //在 C++11中可以使用大括號語法初始化數(shù)組類型的成員變量
               A() : arr{0}
               {
               }
           
           public:
               int arr[4];
           };

          如果你有興趣,我們可以更進一步:

          在 C++ 98/03 標準中,對類的成員必須使用 static const 修飾,而且類型必須是整型 (包括 bool、 char、 int、 long 等),這樣才能使用這種初始化語法:

           //C++98/03 在類定義處初始化成員變量
           class A
           {
           public:
               //T 的類型必須是整型,且必須使用 static const 修飾
               static const T t = 某個整型值;
           };

          在 C++11 標準中就沒有這種限制了,我們可以使用花括號(即{})對任意類型的變 量進行初始化,而且不用是 static 類型:

           //C++ 11 在類定義處初始化成員變量
           class A
           {
           public:
               //有沒有一種Java初始化類成員變量的即視感^ _ ^
               bool ma{true};
               int mb{2019};
               std::string mc{"helloworld"};
           };

          當然,在實際開發(fā)中,建議還是將這些成員變量的初始化統(tǒng)一寫到構造函數(shù)的初始化列表中,方便閱讀和維護代碼。

          案例 2:注解標簽

          C++ 14 引入了 [[deprecated]] 標簽來表示一個函數(shù)或者類型等已被棄用,在使用這些被棄用的函數(shù)或者類型并編譯時, 編譯器會給出相應的警告, 有的編譯器直接生成編譯錯誤:

           [[deprecated]] void funcX();

          這個標簽在實際開發(fā)中非常有用,尤其在設計一些庫代碼時,如果庫作者希望某個函數(shù)或者類型不想再被用戶使用,則可以使用該標注標記。當然,我們也可以使用如下語法給出編譯時的具體警告或者出錯信息:

           [[deprecated("use funY instead")]] void funcX();

          有如下代碼:

           #include <iostream>
           [[deprecated("use funcY instead")]] void funcX()
           {
               //實現(xiàn)省略
           }
           
           int main()
           {
               funcX();
               return 0;
           }

          若在 main 函數(shù)中調用被標記為 deprecated 的函數(shù) funcX,則在 gcc/g++7.3 中編譯時會得到如下警告信息:

           [root@myaliyun testmybook]# g++ -g -o test_attributes test_attributes.cpp
           test_attributes.cpp: In function ‘int main()’:
           test_attributes.cpp:10:11: warning: ‘void funcX()’ is deprecated: use funcY instead
           [-Wdeprecated-declarations]
           funcX();
           ^
           test_attributes.cpp:3:42: note: declared here
           [[deprecated("use funcY instead")]] void funcX()

          Java 開發(fā)者對這個標注應該再熟悉不過了。在 Java 中使用@Deprecated 標注可以達到同樣的效果,這大概是 C++標準委員“拖欠”廣大 C++開發(fā)者太久的一個特性吧。

          C++ 17 提供了三個實用注解:[[fallthrough]]、 [[nodiscard]] 和 [[maybe_unused]],這里 逐一介紹它們的用法。

          [[fallthrough]] 用于 switch-case 語句中,在某個 case 分支執(zhí)行完畢后如果沒有 break 語句,則編譯器可能會給出一條警告。但有時這可能是開發(fā)者有意為之的。為了讓編譯器明確知道開發(fā)者的意圖,可以在需要某個 case 分支被“貫穿”的地方(上一個 case 沒有break 語句)顯式設置 [[fallthrough]] 標記。代碼示例如下:

           switch (type)
           {
           case 1:
               func1();
               //這個位置缺少 break 語句,且沒有 fallthrough 標注,
               //可能是一個邏輯錯誤,在編譯時編譯器可能會給出警告,以提醒修改
           
           case 2:
               func2();
               //這里也缺少 break 語句,但是使用了 fallthrough 標注,
               //說明是開發(fā)者有意為之的,編譯器不會給出任何警告
               [[fallthrough]];
           
           case 3:
               func3();
           }

          注意:在 gcc/g++中, [[fallthrough]] 后面的分號不是必需的,在 Visual Studio 中必須加上分號,否則無法編譯通過。

          熟悉 Golang 的讀者,可能對 fallthrough 這一語法特性非常熟悉, Golang 中在 switch-case 后加上 fallthrough,是一個常用的告訴編譯器意圖的語法規(guī)則。代碼示例如下:

           //以下是 Golang 語法
           s := "abcd"
           switch s[3] {
               case 'a':
                   fmt.Println("The integer was <= 4")
                   fallthrough
           
               case 'b':
                   fmt.Println("The integer was <= 5")
                   fallthrough
           
               case 'c':
                   fmt.Println("The integer was <= 6")
           
               default:
                   fmt.Println("default case")
           }

          [[nodiscard]] 一般用于修飾函數(shù),告訴函數(shù)調用者必須關注該函數(shù)的返回值(即不能丟棄該函數(shù)的返回值)。如果函數(shù)調用者未將該函數(shù)的返回值賦值給一個變量,則編譯器會給出一個警告。例如,假設有一個網(wǎng)絡連接函數(shù) connect,我們通過返回值明確說明了連接是否建立成功,則為了防止調用者在使用時直接將該值丟棄,我們可以將該函數(shù)使用 [[nodiscard]] 標記:

           [[nodiscard]] int connect(const char* address, short port)
           {
               //實現(xiàn)省略
           }
           
           int main()
           {
               //忽略了connect函數(shù)的返回值,編譯器會給出一個警告
               connect("127.0.0.1", 8888);
               return 0;
           }

          在 C++ 20 中,對于諸如 operator new()、 std::allocate()等庫函數(shù)均使用了 [[nodiscard]] 進行標記,以強調必須使用這些函數(shù)的返回值。

          再來看另外一個標記。

          在通常情況下,編譯器會對程序代碼中未使用的函數(shù)或變量給出警告,另一些編譯器干脆不允許通過編譯。在 C++ 17 之前,程序員為了消除這些未使用的變量帶來的編譯警告或者錯誤,要么修改編譯器的警告選項設置,要么定義一個類似于 UNREFERENCED_PARAMETER 的宏來顯式調用這些未使用的變量一次,以消除編譯警告或錯誤:

           #define UNREFERENCED_PARAMETER(x) x
           
           int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
           {
               //C++17之前為了消除編譯器對未使用的變量hPrevInstance、lpCmdLine給出的警告,我們可以這么做
               UNREFERENCED_PARAMETER(hPrevInstance);
               UNREFERENCED_PARAMETER(lpCmdLine);
               //無關代碼省略
           }

          以上代碼節(jié)選自一個標準 Win32 程序的結構,其中的函數(shù)參數(shù) hPrevInstance 和 lpCmdLine 一般不會被用到,編譯器會給出警告。為了消除這類警告,這里定義了一個宏 UNREFERENCED_PARAMETER 并進行調用,造成這兩個參數(shù)被使用的假象。

          C++17 有了 [[maybe_unused]] 注解之后,我們就再也不需要這類宏來“欺騙”編譯器了。以上代碼使用該注解后可以修改如下:

           int APIENTRY wWinMain(HINSTANCE hInstance,
                                 [[maybe_unused]] HINSTANCE hPrevInstance,
                                 [[maybe_unused]] LPWSTR lpCmdLine,
                                 int nCmdShow)
           {
               //無關代碼省略
           }

          案例 3:final、 override 關鍵字和 =default、 =delete 語法

          3.1 final 關鍵字

          在 C++11 之前,我們沒有特別好的方法阻止一個類被其他類繼承,到了 C++11 有了 final 關鍵字我們就可以做到了。final 關鍵字修飾一個類,這個類將不允許被繼承,這在其他語言(如 Java)中早就實現(xiàn)了。在 C++ 11 中, final 關鍵字要寫在類名的后面,這在其他語言中是寫在 class 關鍵字前面的。示例如下:

           class A final
           {
           };
           
           class B : A
           {
           };

          由于類 A 被聲明成 final, B 繼承 A, 所以編譯器會報如下錯誤提示類 A 不能被繼承:

           error C3246: 'B' : cannot inherit from 'A' as it has been declared as 'final'

          3.2 override 關鍵字

          C++98/03 語法規(guī)定,在父類中加了 virtual 關鍵字的方法可以被子類重寫,子類重寫該方法時可以加或不加 virtual 關鍵字,例如下面這樣:

           class A
           {
           protected:
               virtual void func(int a, int b)
               {
               }
           };
           
           class B : A
           {
           protected:
               virtual void func(int a, int b)
               {
               }
           };
           
           class C : B
           {
           protected:
               void func(int a, int b)
               {
               }
           };

          這種寬松的規(guī)定可能會帶來以下兩個問題。

          • 當我們閱讀代碼時,無論子類重寫的方法是否添加了 virtual 關鍵字,我們都無法 直觀地確定該方法是否是重寫的父類方法。
          • 如果我們在子類中不小心寫錯了需要重寫的方法的函數(shù)簽名(可能是參數(shù)類型、 個數(shù)或返回值類型),這個方法就會變成一個獨立的方法,這可能會違背我們重寫 父類某個方法的初衷,而編譯器在編譯時并不會檢查到這個錯誤。

          為了解決以上兩個問題, C++11 引進了 override 關鍵字,其實 override 關鍵字并不是新語法,在 Java 等其他編程語言中早就支持。類方法被 override 關鍵字修飾,表明該方法重寫了父類的同名方法,加了該關鍵字后,編譯器會在編譯階段做相應的檢查,如果其父類不存在相同簽名格式的類方法,編譯器就會給出相應的錯誤提示。

          情形一,父類不存在,子類標記了 override 的方法:

           class A
           {
           };
           
           class B : A
           {
           protected:
               void func(int k, int d) override
               {
               }
           };

          由于在父類 A 中沒有 func 方法,所以編譯器會提示錯誤:

           error C3668: 'B::func' : method with override specifier 'override' did not override
           any base class methods

          情形二,父類存在,子類標記了 override 的方法,但函數(shù)簽名不一致:

           class A
           {
           protected:
               virtual int func(int k, int d)
               {
                   return 0;
               }
           };
           
           class B : A
           {
           protected:
               virtual void func(int k, int d) override
               {
               }
           };

          上述代碼編譯器會報同樣的錯誤。正確的代碼如下:

           class A
           {
           protected:
               virtual void func(int k, int d)
               {
               }
           };
           
           class B : A
           {
           protected:
               virtual void func(int k, int d) override
               {
               }
           };

          3.3 default 語法

          如果一個 C++類沒有顯式給出構造函數(shù)、析構函數(shù)、拷貝構造函數(shù)、 operator= 這幾類函數(shù)的實現(xiàn),則在需要它們時,編譯器會自動生成;或者,在給出這些函數(shù)的聲明時,如果沒有給出其實現(xiàn),則編譯器在鏈接時會報錯。如果使用=default 標記這類函數(shù),則編譯器會給出默認的實現(xiàn)。來看一個例子:

           class A
           {
           };
           
           int main()
           {
               A a;
               return 0;
           }

          這樣的代碼是可以編譯通過的,因為編譯器默認生成 A 的一個無參構造函數(shù),假設我們現(xiàn)在向 A 提供一個有參構造函數(shù):

           class A
           {
           public:
               A(int i)
               {
               }
           };
           
           int main()
           {
               A a;
               return 0;
           }

          這時,編譯器就不會自動生成默認的無參構造函數(shù)了,這段代碼會編譯出錯,提示 A 沒有合適的無參構造函數(shù):

           error C2512: 'A' : no appropriate default constructor available

          我們這時可以手動為 A 加上無參構造函數(shù), 也可以使用=default 語法強行讓編譯器自己生成:

           class A
           {
           public:
               A() = default;
               A(int i)
               {
               }
           };
           
           int main()
           {
               A a;
               return 0;
           }

          =default 最大的作用可能是在開發(fā)中簡化了構造函數(shù)中沒有實際初始化代碼的寫法,尤其是聲明和實現(xiàn)分別屬于.h 和.cpp 文件。例如,對于類 A,其頭文件為 a.h,其實現(xiàn)文件為 a.cpp,則正常情況下我們需要在 a.cpp 文件中寫其構造函數(shù)和析構函數(shù)的實現(xiàn)(可能沒有實際的構造和析構邏輯):

           //a.h
           class A
           {
           public:
               A();
               ~A();
           };
           
           //a.cpp
           #include "a.h"
           
           A::A()
           {
           }
           
           A::~A()
           {
           }

          可以發(fā)現(xiàn),即使在 A 的構造函數(shù)和析構函數(shù)中什么邏輯也沒有,我們還是不得不在 a.cpp 中寫上構造函數(shù)和析構函數(shù)的實現(xiàn)。有了=default 關鍵字,我們就可以在 a.h 中直接寫成:

           //a.h
           class A
           {
           public:
               A() = default;
               ~A() = default;
           };
           
           //a.cpp
           #include "a.h"
           //在 cpp 文件中就不用再寫 A 的構造函數(shù)和析構函數(shù)的實現(xiàn)了

          3.4 =delete 語法

          既然有強制讓編譯器生成構造函數(shù)、析構函數(shù)、拷貝構造函數(shù)、 operator=的語法,那么也應該有禁止編譯器生成這些函數(shù)的語法,沒錯,就是 =delete。在 C++ 98/03 規(guī)范中, 如果我們想讓一個類不能被拷貝(即不能調用其拷貝構造函數(shù)),則可以將其拷貝構造函數(shù)和 operator=函數(shù)定義成 private 的:

           class A
           {
           public:
               A() = default;
               ~A() = default;
           
           private:
               A(const A& a)
               {
               }
               
               A& operator =(const A& a)
               {
               }
           };
           
           int main()
           {
               A a1;
               A a2(a1);
               A a3;
               a3 = a1;
               return 0;
           }

          通過以上代碼利用 a1 構造 a2 時,編譯器會提示錯誤:

           error C2248: 'A::A' : cannot access private member declared in class 'A'
           error C2248: 'A::operator =' : cannot access private member declared in class 'A'

          我們利用這種方式間接實現(xiàn)了一個類不能被拷貝的功能,這也是繼承自 boost::noncopyable 的類不能被拷貝的實現(xiàn)原理?,F(xiàn)在有了=delete語法,我們直接使用該語法禁止編譯器生成這兩個函數(shù)即可:

           class A
           {
           public:
               A() = default;
               ~A() = default;
           public:
               A(const A& a) = delete;
               A& operator =(const A& a) = delete;
           };
           
           int main()
           {
               A a1;
               //A a2(a1);
               A a3;
               //a3 = a1;
               return 0;
           }

          一般在一些工具類中, 我們不需要用到構造函數(shù)、 析構函數(shù)、 拷貝構造函數(shù)、 operator= 這 4 個函數(shù),為了防止編譯器自己生成,同時為了減小生成的可執(zhí)行文件的體積,建議使用=delete 語法禁止編譯器為這 4 個函數(shù)生成默認的實現(xiàn)代碼,例如:

           //這是一個字符轉碼工具類
           class EncodeUtil
           {
           public:
               static std::wstring AnsiiToUnicode(const std::string& strAnsii);
               static std::string UnicodeToAnsii(const std::wstring& strUnicode);
               static std::string AnsiiToUtf8(const std::string& strAnsii);
               static std::string Utf8ToAnsii(const std::string& strUtf8);
               static std::string UnicodeToUtf8(const std::wstring& strUnicode);
               static std::wstring Utf8ToUnicode(const std::string& strUtf8);
               
           private:
               EncodeUtil() = delete;
               ~EncodeUtil() = delete;
               EncodeUtil(const EncodeUtil& rhs) = delete;
               EncodeUtil& operator=(const EncodeUtil& rhs) = delete;
           };

          案例 4:對多線程的支持

          我們來看一個稍微復雜一點的例子。

          在 C++11 之前,由于 C++98/03 本身缺乏對線程和線程同步原語的支持,我們要寫一個生產(chǎn)者消費者邏輯要這么寫。

          在 Windows 上:

           /**
            * RecvMsgTask.h
            */
           class CRecvMsgTask : public CThreadPoolTask
           {
           public:
               CRecvMsgTask(void);
               ~CRecvMsgTask(void);
           
           public:
               virtual int Run();
               virtual int Stop();
               virtual void TaskFinish();
           
               BOOL AddMsgData(CBuffer* lpMsgData);
           
           private:
               BOOL HandleMsg(CBuffer* lpMsg);
           
           private:
               HANDLE                m_hEvent;
               CRITICAL_SECTION      m_csItem;
               HANDLE                m_hSemaphore;
               std::vector<CBuffer*> m_arrItem;
           };
           
           /**
            * RecvMsgTask.cpp
            */
           CRecvMsgTask::CRecvMsgTask(void)
           {
               ::InitializeCriticalSection(&m_csItem);
               m_hSemaphore = ::CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
               m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
           }
           
           CRecvMsgTask::~CRecvMsgTask(void)
           {
               ::DeleteCriticalSection(&m_csItem);
           
               if (m_hSemaphore != NULL)
               {
                   ::CloseHandle(m_hSemaphore);
                   m_hSemaphore = NULL;
               }
           
               if (m_hEvent != NULL)
               {
                   ::CloseHandle(m_hEvent);
                   m_hEvent = NULL;
               }
           }
           
           int CRecvMsgTask::Run()
           {
               HANDLE hWaitEvent[2];
               DWORD dwIndex;
               CBuffer * lpMsg;
           
               hWaitEvent[0] = m_hEvent;
               hWaitEvent[1] = m_hSemaphore;
           
               while (1)
               {
                   dwIndex = ::WaitForMultipleObjects(2, hWaitEvent, FALSE, INFINITE);
           
                   if (dwIndex == WAIT_OBJECT_0)
                       break;
           
                   lpMsg = NULL;
           
                   ::EnterCriticalSection(&m_csItem);
                   if (m_arrItem.size() > 0)
                   {
                       //消費者從隊列m_arrItem中取出任務執(zhí)行
                       lpMsg = m_arrItem[0];
                       m_arrItem.erase(m_arrItem.begin() + 0);
                   }
                   ::LeaveCriticalSection(&m_csItem);
           
                   if (NULL == lpMsg)
                       continue;
           
                   //處理任務
                   HandleMsg(lpMsg);
           
                   delete lpMsg;
               }
           
               return 0;
           }
           
           int CRecvMsgTask::Stop()
           {
               m_HttpClient.SetCancalEvent();
               ::SetEvent(m_hEvent);
               return 0;
           }
           
           void CRecvMsgTask::TaskFinish()
           {
           }
           
           //生產(chǎn)者調用這個方法將Task放入隊列m_arrItem中
           BOOL CRecvMsgTask::AddMsgData(CBuffer * lpMsgData)
           {
               if (NULL == lpMsgData)
                   return FALSE;
           
               ::EnterCriticalSection(&m_csItem);
               m_arrItem.push_back(lpMsgData);
               ::LeaveCriticalSection(&m_csItem);
           
               ::ReleaseSemaphore(m_hSemaphore, 1, NULL);
           
               return TRUE;
           }

          在 Linux 下:

           #include <pthread.h>
           #include <errno.h>
           #include <unistd.h>
           #include <list>
           #include <semaphore.h>
           #include <iostream>
           
           class Task
           {
           public:
               Task(int taskID)
               {
                   this->taskID = taskID;
               }
               
               void doTask()
               {
                   std::cout << "handle a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl; 
               }
               
           private:
               int taskID;
           };
           
           pthread_mutex_t  mymutex;
           std::list<Task*> tasks;
           pthread_cond_t   mycv;
           
           void* consumer_thread(void* param)
           {   
               Task* pTask = NULL;
               while (true)
               {
                   pthread_mutex_lock(&mymutex);
                   while (tasks.empty())
                   {               
                       //如果獲得了互斥鎖,但是條件不合適的話,pthread_cond_wait會釋放鎖,不往下執(zhí)行。
                       //當發(fā)生變化后,條件合適,pthread_cond_wait將直接獲得鎖。
                       pthread_cond_wait(&mycv, &mymutex);
                   }
                   
                   pTask = tasks.front();
                   tasks.pop_front();
           
                   pthread_mutex_unlock(&mymutex);
                   
                   if (pTask == NULL)
                       continue;
           
                   pTask->doTask();
                   delete pTask;
                   pTask = NULL;       
               }
               
               return NULL;
           }
           
           void* producer_thread(void* param)
           {
               int taskID = 0;
               Task* pTask = NULL;
               
               while (true)
               {
                   pTask = new Task(taskID);
                       
                   pthread_mutex_lock(&mymutex);
                   tasks.push_back(pTask);
                   std::cout << "produce a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl; 
                   
                   pthread_mutex_unlock(&mymutex);
                   
                   //釋放信號量,通知消費者線程
                   pthread_cond_signal(&mycv);
                   
                   taskID ++;
           
                   //休眠1秒
                   sleep(1);
               }
               
               return NULL;
           }
           
           int main()
           {
               pthread_mutex_init(&mymutex, NULL);
               pthread_cond_init(&mycv, NULL);
           
               //創(chuàng)建5個消費者線程
               pthread_t consumerThreadID[5];
               for (int i = 0; i < 5; ++i)
                   pthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);
               
               //創(chuàng)建一個生產(chǎn)者線程
               pthread_t producerThreadID;
               pthread_create(&producerThreadID, NULL, producer_thread, NULL);
           
               pthread_join(producerThreadID, NULL);
               
               for (int i = 0; i < 5; ++i)
                   pthread_join(consumerThreadID[i], NULL);
               
               pthread_cond_destroy(&mycv);
               pthread_mutex_destroy(&mymutex);
           
               return 0;
           }

          怎么樣?上述代碼如果對于新手來說,望而卻步。

          為了實現(xiàn)這樣的功能在 Windows 上你需要掌握線程如何創(chuàng)建、線程同步對象 CriticalSection、Event、Semaphore、WaitForSingleObject/WaitForMultipleObjects 等操作系統(tǒng)對象和 API。

          在 Linux 上需要掌握線程創(chuàng)建,你需要了解線程創(chuàng)建、互斥體、條件變量。

          對于需要支持多個平臺的開發(fā),需要開發(fā)者同時熟悉上述原理并編寫多套適用不同平臺的代碼。

          C++11 的線程庫改變了這個現(xiàn)狀,現(xiàn)在你只需要掌握 std::thread、std::mutex、std::condition_variable 少數(shù)幾個線程同步對象即可,同時使用這些對象編寫出來的代碼也可以跨平臺。示例如下:

           #include <thread>
           #include <mutex>
           #include <condition_variable>
           #include <list>
           #include <iostream>
           
           class Task
           {
           public:
               Task(int taskID)
               {
                   this->taskID = taskID;
               }
               
               void doTask()
               {
                   std::cout << "handle a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl; 
               }
               
           private:
               int taskID;
           };
           
           std::mutex                mymutex;
           std::list<Task*>          tasks;
           std::condition_variable   mycv;
           
           void* consumer_thread()
           {   
               Task* pTask = NULL;
               while (true)
               {
                   std::unique_lock<std::mutex> guard(mymutex);
                   while (tasks.empty())
                   {               
                       //如果獲得了互斥鎖,但是條件不合適的話,pthread_cond_wait會釋放鎖,不往下執(zhí)行。
                       //當發(fā)生變化后,條件合適,pthread_cond_wait將直接獲得鎖。
                       mycv.wait(guard);
                   }
                   
                   pTask = tasks.front();
                   tasks.pop_front();
                   
                   if (pTask == NULL)
                       continue;
           
                   pTask->doTask();
                   delete pTask;
                   pTask = NULL;       
               }
               
               return NULL;
           }
           
           void* producer_thread()
           {
               int taskID = 0;
               Task* pTask = NULL;
               
               while (true)
               {
                   pTask = new Task(taskID);
                       
                   //使用括號減小guard鎖的作用范圍
                   {
                       std::lock_guard<std::mutex> guard(mymutex);
                       tasks.push_back(pTask);
                       std::cout << "produce a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl; 
                   }
                   
                   //釋放信號量,通知消費者線程
                   mycv.notify_one();
                   
                   taskID ++;
           
                   //休眠1秒
                   std::this_thread::sleep_for(std::chrono::seconds(1));
               }
               
               return NULL;
           }
           
           int main()
           {
               //創(chuàng)建5個消費者線程
               std::thread consumer1(consumer_thread);
               std::thread consumer2(consumer_thread);
               std::thread consumer3(consumer_thread);
               std::thread consumer4(consumer_thread);
               std::thread consumer5(consumer_thread);
               
               //創(chuàng)建一個生產(chǎn)者線程
               std::thread producer(producer_thread);
           
               producer.join();
               consumer1.join();
               consumer2.join();
               consumer3.join();
               consumer4.join();
               consumer5.join();
           
               return 0;
           }

          感覺如何?代碼既簡潔又統(tǒng)一。

          這就是 C++11 之后使用 Modern C++ 開發(fā)的效率!

          C++11 之后的 C++ 更像一門新的語言。

          當 C++11 的編譯器發(fā)布之后(Visual Studio 2013、g++4.8),我第一時間更新了我的編譯器,同時把我們的項目使用了 C++11 特性進行了改造。

          當然,例子還有很多,限于文章篇幅,這里就列舉 4 個案例。

          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  麻豆三级 | 欧美性爱免费在线视频海量版 | 亚洲污视频在线观看 | 人妻体内射精一区二区 | 亚洲家庭乱轮五月天 |