C++11經常使用特性的使用經驗總結

概述及目錄(原創博客,版權全部,轉載請註明出處 http://www.cnblogs.com/feng-sc)

  C++11已經出來好久了,網上也早有不少優秀的C++11新特性的總結文章,在編寫本博客以前,博主在工做和學習中學到的關於C++11方面的知識,也得益於不少其餘網友的總結。本博客文章是在學習的基礎上,加上博主在平常工做中的使用C++11的一些總結、經驗和感悟,整理出來,分享給你們,但願對各位讀者有幫助,文章中的總結可能存在不少不完整或有錯誤的地方,也但願讀者指出。你們能夠根據以下目錄跳到本身須要的章節。html

  一、關鍵字及新語法java

    1.一、auto關鍵字及用法linux

    1.二、nullptr關鍵字及用法ios

    1.三、for循環語法c++

  二、STL容器web

    2.一、std::array算法

    2.二、std::forward_list編程

    2.三、std::unordered_map數組

    2.四、std::unordered_set數據結構

  三、多線程

    3.一、std::thread

    3.二、st::atomic

    3.三、std::condition_variable

  四、智能指針內存管理

    4.一、std::shared_ptr

    4.二、std::weak_ptr

  五、其餘

    5.一、std::function、std::bind封裝可執行對象

    5.二、lamda表達式

一、關鍵字及新語法

  C++11相比C++98增長了許多關鍵字及新的語法特性,不少人以爲這些語法無關緊要,沒有新特性也能夠用傳統C++去實現。

  也許吧,但我的對待新技術老是抱着渴望而熱衷的態度對待,也許正如不少人所想,用傳統語法也能夠實現,但新技術可讓你的設計更完美。這就如同在原來的維度裏,你要完成一件事情,須要不少個複雜的步驟,但在新語法特性裏,你能夠從另外的維度,很乾脆,直接就把原來很複雜的問題用很簡單的方法解決了,我想着就是新的技術帶來的一些編程體驗上很是好的感受。你們也不要以爲代碼寫得越複雜就先顯得越牛B,有時候在理解的基礎上,儘可能選擇「站在巨人的肩膀上」,可能你會站得更高,也看得更遠。

  本章重點總結一些經常使用c++11新語法特色。後續會在本人理解的基礎上,會繼續在本博客內更新或增長新的小章節。

1.一、auto關鍵字及用法

  A、auto關鍵字能作什麼?

  auto並無讓C++成爲弱類型語言,也沒有弱化變量什麼,只是使用auto的時候,編譯器根據上下文狀況,肯定auto變量的真正類型。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
auto AddTest(int a, int b) { return a + b; } int main() { auto index = 10; auto str = "abc"; auto ret = AddTest(1,2); std::cout << "index:" << index << std::endl; std::cout << "str:" << str << std::endl; std::cout << "res:" << ret << std::endl; }

  是的,你沒看錯,代碼也沒錯,auto在C++14中能夠做爲函數的返回值,所以auto AddTest(int a, int b)的定義是沒問題的。

  運行結果:

    

  B、auto不能作什麼?

  auto做爲函數返回值時,只能用於定義函數,不能用於聲明函數。

//Test.h 示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#pragma once
class Test
{
public:
    auto TestWork(int a ,int b);
};

  以下函數中,在引用頭文件的調用TestWork函數是,編譯沒法經過。

  但若是把實現寫在頭文件中,能夠編譯經過,由於編譯器能夠根據函數實現的返回值肯定auto的真實類型。若是讀者用過inline類成員函數,這個應該很容易明白,此特性與inline類成員函數相似。

//Test.h 示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#pragma
once class Test { public: auto TestWork(int a, int b) { return a + b; } };

 

1.二、nullptr關鍵字及用法

  爲何須要nullptr? NULL有什麼毛病?

  咱們經過下面一個小小的例子來發現NULL的一點問題:

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void TestWork(int index)
    {
        std::cout << "TestWork 1" << std::endl;
    }
    void TestWork(int * index)
    {
        std::cout << "TestWork 2" << std::endl;
    }
};

int main()
{
    Test test;
    test.TestWork(NULL);
    test.TestWork(nullptr);
}

  運行結果:

     

  NULL在c++裏表示空指針,看到問題了吧,咱們調用test.TestWork(NULL),其實指望是調用的是void TestWork(int * index),但結果調用了void TestWork(int index)。但使用nullptr的時候,咱們能調用到正確的函數。

 

1.三、for循環語法

  習慣C#或java的同事以前使用C++的時候曾吐槽C++ for循環沒有想C#那樣foreach的用法,是的,在C++11以前,標準C++是沒法作到的。熟悉boost庫讀者可能知道boost裏面有foreach的宏定義BOOST_FOREACH,但我的以爲看起並非那麼美觀。

  OK,咱們直接以簡單示例看看用法吧。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int
main() { int numbers[] = { 1,2,3,4,5 }; std::cout << "numbers:" << std::endl; for (auto number : numbers) { std::cout << number << std::endl; } }

   以上用法不只僅侷限於數據,STL容器都一樣適用。

 

二、STL容器

  C++11在STL容器方面也有所增長,給人的感受就是愈來愈完整,愈來愈豐富的感受,可讓咱們在不一樣場景下能選擇跟具合適的容器,提升咱們的效率。

  本章節總結C++11新增的一些容器,以及對其實現作一些簡單的解釋。

2.一、std::array

  我的以爲std::array跟數組並無太大區別,對於多維數據使用std::array,我的反而有些不是很習慣吧。

  std::array相對於數組,增長了迭代器等函數(接口定義可參考C++官方文檔)。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <array>
int main()
{
    std::array<int, 4> arrayDemo = { 1,2,3,4 };
    std::cout << "arrayDemo:" << std::endl;
    for (auto itor : arrayDemo)
    {
        std::cout << itor << std::endl;
    }
    int arrayDemoSize = sizeof(arrayDemo);
    std::cout << "arrayDemo size:" << arrayDemoSize << std::endl;
    return 0;
}

  運行結果:

     

  打印出來的size和直接使用數組定義結果是同樣的。

 

2.二、std::forward_list

  std::forward_list爲從++新增的線性表,與list區別在於它是單向鏈表。咱們在學習數據結構的時候都知道,鏈表在對數據進行插入和刪除是比順序存儲的線性表有優點,所以在插入和刪除操做頻繁的應用場景中,使用list和forward_list比使用array、vector和deque效率要高不少。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <forward_list>
int main()
{
    std::forward_list<int> numbers = {1,2,3,4,5,4,4};
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    numbers.remove(4);
    std::cout << "numbers after remove:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    return 0;
}

  運行結果:

    

2.三、std::unordered_map

  std::unordered_map與std::map用法基本差很少,但STL在內部實現上有很大不一樣,std::map使用的數據結構爲二叉樹,而std::unordered_map內部是哈希表的實現方式,哈希map理論上查找效率爲O(1)。但在存儲效率上,哈希map須要增長哈希表的內存開銷。

  下面代碼爲C++官網實例源碼實例:

//webset address: http://www.cplusplus.com/reference/unordered_map/unordered_map/bucket_count/
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
    std::unordered_map<std::string, std::string> mymap =
    {
        { "house","maison" },
        { "apple","pomme" },
        { "tree","arbre" },
        { "book","livre" },
        { "door","porte" },
        { "grapefruit","pamplemousse" }
    };
    unsigned n = mymap.bucket_count();
    std::cout << "mymap has " << n << " buckets.\n";
    for (unsigned i = 0; i<n; ++i) 
    {
        std::cout << "bucket #" << i << " contains: ";
        for (auto it = mymap.begin(i); it != mymap.end(i); ++it)
            std::cout << "[" << it->first << ":" << it->second << "] ";
        std::cout << "\n";
    }
    return 0;
}

  運行結果:

    

  運行結果與官網給出的結果不同。實驗證實,不一樣編譯器編譯出來的結果不同,以下爲linux下gcc 4.6.3版本編譯出來的結果。或許是由於使用的哈希算法不同,我的沒有深究此問題。

  

2.四、std::unordered_set

  std::unordered_set的數據存儲結構也是哈希表的方式結構,除此以外,std::unordered_set在插入時不會自動排序,這都是std::set表現不一樣的地方。

  咱們來測試一下下面的代碼:  

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <iostream> #include <string> #include <unordered_set> #include <set> int main() { std::unordered_set<int> unorder_set; unorder_set.insert(7); unorder_set.insert(5); unorder_set.insert(3); unorder_set.insert(4); unorder_set.insert(6); std::cout << "unorder_set:" << std::endl; for (auto itor : unorder_set) { std::cout << itor << std::endl; } std::set<int> set; set.insert(7); set.insert(5); set.insert(3); set.insert(4); set.insert(6); std::cout << "set:" << std::endl; for (auto itor : set) { std::cout << itor << std::endl; } }

  運行結果:

    

三、多線程

  在C++11之前,C++的多線程編程均需依賴系統或第三方接口實現,必定程度上影響了代碼的移植性。C++11中,引入了boost庫中的多線程部份內容,造成C++標準,造成標準後的boost多線程編程部分接口基本沒有變化,這樣方便了之前使用boost接口開發的使用者切換使用C++標準接口,把容易把boost接口升級爲C++接口。

  咱們經過以下幾部分介紹C++11多線程方面的接口及使用方法。

3.一、std::thread

  std::thread爲C++11的線程類,使用方法和boost接口同樣,很是方便,同時,C++11的std::thread解決了boost::thread中構成參數限制的問題,我想着都是得益於C++11的可變參數的設計風格。

  咱們經過以下代碼熟悉下std::thread使用風格。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <thread> void threadfun1() { std::cout << "threadfun1 - 1\r\n" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "threadfun1 - 2" << std::endl; } void threadfun2(int iParam, std::string sParam) { std::cout << "threadfun2 - 1" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "threadfun2 - 2" << std::endl; } int main() { std::thread t1(threadfun1); std::thread t2(threadfun2, 10, "abc"); t1.join(); std::cout << "join" << std::endl; t2.detach(); std::cout << "detach" << std::endl; }

  運行結果:

    

  有以上輸出結果能夠得知,t1.join()會等待t1線程退出後才繼續往下執行,t2.detach()並不會並不會把,detach字符輸出後,主函數退出,threadfun2還未執行完成,可是在主線程退出後,t2的線程也被已經被強退出。

 

3.二、std::atomic

  std::atomic爲C++11分裝的原子數據類型。

  什麼是原子數據類型?

  從功能上看,簡單地說,原子數據類型不會發生數據競爭,能直接用在多線程中而沒必要咱們用戶對其進行添加互斥資源鎖的類型。從實現上,你們能夠理解爲這些原子類型內部本身加了鎖。

  咱們下面經過一個測試例子說明原子類型std::atomic_int的特色。

  下面例子中,咱們使用10個線程,把std::atomic_int類型的變量iCount從100減到1。

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <thread> #include <atomic> #include <stdio.h> std::atomic_bool bIsReady = false; std::atomic_int iCount = 100; void threadfun1() { if (!bIsReady) { std::this_thread::yield(); } while (iCount > 0) { printf("iCount:%d\r\n", iCount--); } } int main() { std::atomic_bool b; std::list<std::thread> lstThread; for (int i = 0; i < 10; ++i) { lstThread.push_back(std::thread(threadfun1)); } for (auto& th : lstThread) { th.join(); } }

  運行結果:

  

   注:屏幕過短的緣由,上面結果沒有截完屏

  從上面的結果能夠看到,iCount的最小結果都是1,單可能不是最後一次打印,沒有小於等於0的狀況,你們能夠代碼複製下來多運行幾遍對比看看。

 

3.三、std::condition_variable

  C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal同樣,可讓線程休眠,直到別喚醒,如今在重新執行。線程等待在多線程編程中使用很是頻繁,常常須要等待一些異步執行的條件的返回結果。

  OK,在此很少解釋,下面咱們經過C++11官網的例子看看。

 1 // webset address: http://www.cplusplus.com/reference/condition_variable/condition_variable/%20condition_variable
 2 // condition_variable example
 3 #include <iostream>           // std::cout
 4 #include <thread>             // std::thread
 5 #include <mutex>              // std::mutex, std::unique_lock
 6 #include <condition_variable> // std::condition_variable
 7 
 8 std::mutex mtx;
 9 std::condition_variable cv;
10 bool ready = false;
11 
12 void print_id(int id) {
13     std::unique_lock<std::mutex> lck(mtx);
14     while (!ready) cv.wait(lck);
15     // ...
16     std::cout << "thread " << id << '\n';
17 }
18 
19 void go() {
20     std::unique_lock<std::mutex> lck(mtx);
21     ready = true;
22     cv.notify_all();
23 }
24 
25 int main()
26 {
27     std::thread threads[10];
28     // spawn 10 threads:
29     for (int i = 0; i<10; ++i)
30         threads[i] = std::thread(print_id, i);
31 
32     std::cout << "10 threads ready to race...\n";
33     go();                       // go!
34 
35     for (auto& th : threads) th.join();
36 
37     return 0;
38 }

  運行結果:

   

  上面的代碼,在14行中調用cv.wait(lck)的時候,線程將進入休眠,在調用33行的go函數以前,10個線程都處於休眠狀態,當22行的cv.notify_all()運行後,14行的休眠將結束,繼續往下運行,最終輸出如上結果。

 

四、智能指針內存管理

  在內存管理方面,C++11的std::auto_ptr基礎上,移植了boost庫中的智能指針的部分實現,如std::shared_ptr、std::weak_ptr等,固然,想boost::thread同樣,C++11也修復了boost::make_shared中構造參數的限制問題。把智能指針添加爲標準,我的以爲真的很是方便,畢竟在C++中,智能指針在編程設計中使用的仍是很是普遍。

  什麼是智能指針?網上已經有不少解釋,我的以爲「智能指針」這個名詞彷佛起得過於「霸氣」,不少初學者看到這個名詞就以爲彷佛很難。

  簡單地說,智能指針只是用對象去管理一個資源指針,同時用一個計數器計算當前指針引用對象的個數,當管理指針的對象增長或減小時,計數器也相應加1或減1,當最後一個指針管理對象銷燬時,計數器爲1,此時在銷燬指針管理對象的同時,也把指針管理對象所管理的指針進行delete操做。

  以下圖所示,簡單話了一下指針、智能指針對象和計數器之間的關係:

  

  

  下面的小章節中,咱們分別介紹經常使用的兩個智能指針std::shared_ptr、std::weak_ptr的用法。

4.一、std::shared_ptr

  std::shared_ptr包裝了new操做符動態分別的內存,能夠自由拷貝複製,基本上是使用最多的一個智能指針類型。

  咱們經過下面例子來了解下std::shared_ptr的用法:

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory> class Test { public: Test() { std::cout << "Test()" << std::endl; } ~Test() { std::cout << "~Test()" << std::endl; } }; int main() { std::shared_ptr<Test> p1 = std::make_shared<Test>(); std::cout << "1 ref:" << p1.use_count() << std::endl; { std::shared_ptr<Test> p2 = p1; std::cout << "2 ref:" << p1.use_count() << std::endl; } std::cout << "3 ref:" << p1.use_count() << std::endl; return 0; }

  運行結果:

  

  從上面代碼的運行結果,須要讀者瞭解的是:

  一、std::make_shared封裝了new方法,boost::make_shared以前的原則是既然釋放資源delete由智能指針負責,那麼應該把new封裝起來,不然會讓人以爲本身調用了new,但沒有調用delete,彷佛與誰申請,誰釋放的原則不符。C++也沿用了這一作法。

  二、隨着引用對象的增長std::shared_ptr<Test> p2 = p1,指針的引用計數有1變爲2,當p2退出做用域後,p1的引用計數變回1,當main函數退出後,p1離開main函數的做用域,此時p1被銷燬,當p1銷燬時,檢測到引用計數已經爲1,就會在p1的析構函數中調用delete以前std::make_shared建立的指針。

4.二、std::weak_ptr

  std::weak_ptr網上不少人說實際上是爲了解決std::shared_ptr在相互引用的狀況下出現的問題而存在的,C++官網對這個只能指針的解釋也很少,那就先甭管那麼多了,讓咱們暫時徹底接受這個觀點。

  std::weak_ptr有什麼特色呢?與std::shared_ptr最大的差異是在賦值是,不會引發智能指針計數增長。

  咱們下面將繼續以下兩點:

  一、std::shared_ptr相互引用會有什麼後果;

  二、std::weak_ptr如何解決第一點的問題。

  A、std::shared_ptr相互引用的問題示例:

   

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory>
class TestB;
class TestA
{
public:
    TestA()
    {
        std::cout << "TestA()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestB> test_ptr)
    {
        m_TestB_Ptr = test_ptr;
    }
    ~TestA()
    {
        std::cout << "~TestA()" << std::endl;
    }
private:
    std::shared_ptr<TestB> m_TestB_Ptr; //TestB的智能指針
}; 

class TestB
{
public:
    TestB()
    {
        std::cout << "TestB()" << std::endl;
    }

    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
        m_TestA_Ptr = test_ptr;
    }
    ~TestB()
    {
        std::cout << "~TestB()" << std::endl;
    }
    std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指針
};


int main()
{
    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
    ptr_a->ReferTestB(ptr_b);
    ptr_b->ReferTestB(ptr_a);
    return 0;
}

  運行結果:

    

  你們能夠看到,上面代碼中,咱們建立了一個TestA和一個TestB的對象,但在整個main函數都運行完後,都沒看到兩個對象被析構,這是什麼問題呢?

  原來,智能指針ptr_a中引用了ptr_b,一樣ptr_b中也引用了ptr_a,在main函數退出前,ptr_a和ptr_b的引用計數均爲2,退出main函數後,引用計數均變爲1,也就是相互引用。

  這等效於說:

    ptr_a對ptr_b說,哎,我說ptr_b,我如今的條件是,你先釋放我,我才能釋放你,這是天生的,造物者決定的,改不了。

    ptr_b也對ptr_a說,個人條件也是同樣,你先釋放我,我才能釋放你,怎麼辦?

  是吧,你們都沒錯,相互引用致使的問題就是釋放條件的衝突,最終也可能致使內存泄漏。

 

  B、std::weak_ptr如何解決相互引用的問題

  咱們在上面的代碼基礎上使用std::weak_ptr進行修改:

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory> class TestB; class TestA { public: TestA() { std::cout << "TestA()" << std::endl; } void ReferTestB(std::shared_ptr<TestB> test_ptr) { m_TestB_Ptr = test_ptr; } void TestWork() { std::cout << "~TestA::TestWork()" << std::endl; } ~TestA() { std::cout << "~TestA()" << std::endl; } private: std::weak_ptr<TestB> m_TestB_Ptr; }; class TestB { public: TestB() { std::cout << "TestB()" << std::endl; } void ReferTestB(std::shared_ptr<TestA> test_ptr) { m_TestA_Ptr = test_ptr; } void TestWork() { std::cout << "~TestB::TestWork()" << std::endl; } ~TestB() { std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock(); tmp->TestWork(); std::cout << "2 ref a:" << tmp.use_count() << std::endl; std::cout << "~TestB()" << std::endl; } std::weak_ptr<TestA> m_TestA_Ptr; }; int main() { std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>(); std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>(); ptr_a->ReferTestB(ptr_b); ptr_b->ReferTestB(ptr_a); std::cout << "1 ref a:" << ptr_a.use_count() << std::endl; std::cout << "1 ref b:" << ptr_a.use_count() << std::endl; return 0; }

  運行結果:

      

  由以上代碼運行結果咱們能夠看到:

  一、全部的對象最後都能正常釋放,不會存在上一個例子中的內存沒有釋放的問題。

  二、ptr_a 和ptr_b在main函數中退出前,引用計數均爲1,也就是說,在TestA和TestB中對std::weak_ptr的相互引用,不會致使計數的增長。在TestB析構函數中,調用std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock(),把std::weak_ptr類型轉換成std::shared_ptr類型,而後對TestA對象進行調用。

  

五、其餘

  本章節介紹的內容若是按照分類來看,也屬於以上語法類別,但感受仍是單獨拿出來總結好些。

  下面小節主要介紹std::function、std::bind和lamda表達式的一些特色和用法,但願對讀者能有所幫助。

5.一、std::function、std::bind封裝可執行對象

  std::bind和std::function也是從boost中移植進來的C++新標準,這兩個語法使得封裝可執行對象變得簡單而易用。此外,std::bind和std::function也能夠結合咱們一下所說的lamda表達式一塊兒使用,使得可執行對象的寫法更加「花俏」。

  咱們下面經過實例一步步瞭解std::function和std::bind的用法:

  Test.h文件

//Test.h 示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class
Test { public: void Add() { } };

  main.cpp文件

//main.cpp 示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <functional> #include <iostream>
#include "Test.h" int add(int a,int b) { return a + b; }

int main() { Test test; test.Add(); return 0; }

  解釋:

    上面代碼中,咱們實現了一個add函數和一個Test類,Test類裏面有一個Test函數也有一個函數Add。

 

  OK,咱們如今來考慮一下這個問題,假如咱們的需求是讓Test裏面的Add由外部實現,如main.cpp裏面的add函數,有什麼方法呢?

  沒錯,咱們能夠用函數指針。

  咱們修改Test.h

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class
Test { public: typedef int(*FunType)(int, int); void Add(FunType fun,int a,int b) { int sum = fun(a, b); std::cout << "sum:" << sum << std::endl; } };

  修改main.cpp的調用

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
....

....
Test test; test.Add(add, 1, 2);
....

  運行結果:

    

 

  到如今爲止,完美了嗎?若是你是Test.h的提供者,你以爲有什麼問題?

  咱們把問題升級,假如add實現是在另一個類內部,以下代碼:

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class
TestAdd { public: int Add(int a,int b) { return a + b; } }; int main() { Test test; //test.Add(add, 1, 2); return 0; }

  假如add方法在TestAdd類內部,那你的Test類沒轍了,由於Test裏的Test函數只接受函數指針。你可能說,這個不是個人問題啊,我是接口的定義者,使用者應該遵循個人規則。但若是如今我是客戶,咱們談一筆生意,就是我要購買使用你的Test類,前提是須要支持我傳入函數指針,也能傳入對象函數,你作不作這筆生意?

  是的,你能夠選擇不作這筆生意。咱們如今再假設你已經好幾個月沒吃肉了(別跟我說你是素食主義者),身邊的蒼蠅肉、蚊子肉啊都不被你吃光了,好不容易等到有機會吃肉,那有什麼辦法呢?

  這個時候std::function和std::bind就幫上忙了。

  咱們繼續修改代碼:

  Test.h

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class
Test { public: void Add(std::function<int(int, int)> fun, int a, int b) { int sum = fun(a, b); std::cout << "sum:" << sum << std::endl; } };

  解釋:

    Test類中std::function<int(int,int)>表示std::function封裝的可執行對象返回值和兩個參數均爲int類型。

  main.cpp

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int
add(int a,int b) { std::cout << "add" << std::endl; return a + b; } class TestAdd { public: int Add(int a,int b) { std::cout << "TestAdd::Add" << std::endl; return a + b; } }; int main() { Test test; test.Add(add, 1, 2); TestAdd testAdd; test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2); return 0; }

  解釋:

    std::bind第一個參數爲對象函數指針,表示函數相對於類的首地址的偏移量;

    testAdd爲對象指針;

    std::placeholders::_1和std::placeholders::_2爲參數佔位符,表示std::bind封裝的可執行對象能夠接受兩個參數。

  

  運行結果:

  

  是的,得出這個結果,你就能夠等着吃肉了,咱們的Test函數在函數指針和類對象函數中都兩種狀況下都完美運行。

  

5.二、lamda表達式

  在衆多的C++11新特性中,我的以爲lamda表達式不只僅是一個語法新特性,對於沒有用過java或C#lamda表達式讀者,C++11的lamda表達式在必定程度上還衝擊着你對傳統C++編程的思惟和想法。

  咱們先從一個簡單的例子來看看lamda表達式:

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
1
int main() 2 { 3 auto add= [](int a, int b)->int{ 4 return a + b; 5 }; 6 int ret = add(1,2); 7 std::cout << "ret:" << ret << std::endl; 8 return 0; 9 }

  解釋:

    第3至5行爲lamda表達式的定義部分

    []:中括號用於控制main函數與內,lamda表達式以前的變量在lamda表達式中的訪問形式;

    (int a,int b):爲函數的形參

    ->int:lamda表達式函數的返回值定義

    {}:大括號內爲lamda表達式的函數體。

  運行結果:

  

  我使用lamda表達式修改5.1中的例子看看:

  main.cpp

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
.....
int main() { Test test; test.Add(add, 1, 2); TestAdd testAdd; test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2); test.Add([](int a, int b)->int { std::cout << "lamda add fun" << std::endl; return a + b; },1,2); return 0; }

  運行結果:

    

相關文章
相關標籤/搜索