學習C++11的一些思考和心得(1):lambda,function,bind和委託

 1.lambda表達式html

lanbda表達式簡單地來說就是一個匿名函數,就是沒有名稱的函數,若是之前有接觸過python或者erlang的人都比較熟悉這個,這個能夠很方便地和STL裏面的算法配合python

    std::for_each(list.begin(),list.end(),
                  [ &tmp,id ](struct ComingVesselInfo *info)
                  {
                    if( info->m_nShipID == id)
                    {
                        tmp = info;
                        return ;
                    }
                  }
                 );

這個是我在項目裏面使用的一段代碼,若是沒有lambda,的代碼則是這樣的:程序員

 

    for ( auto iter = list.begin();
           iter != list.end();
           ++iter)
    {
        if ( iter->m_nShipID == id)
        {
            tmp = iter;
        }
    }

從上面的代碼比較並無看出lambda有什麼用,反而喪失了一點代碼的可讀性,可是我以爲lambda的引入不是咱們能夠在函數內直接很方便地寫函數,而是能夠很好地跟STL algorithm庫配合,在咱們的項目中,我不多使用到STL algorithm庫,由於大部分算法函數都有下面的兩個問題:算法

1.沒法獲取返回值閉包

2.沒法傳入其餘的參數(好比上面的for_each的例子,若是沒有lambda,並無辦法把tmp和id傳入)函數

lambda的引入解決了我在項目中使用STL的algorithm的顧慮性能

順便說下,上面的auto也是C++11的新標準,就是自動推導右邊數據的類型測試

 

2.function和bind以及委託spa


在C#裏面,咱們常常會看到使用委託的,C#的委託語法是這樣的:.net

public delegate void Ticket();

這樣的話,咱們只要聲明一個Ticket t,這個只要符合functionname()這個形式的參數,不論是static 函數仍是類成員函數,均可以賦值給t,而且能夠經過t()來調用該函數或者成員函數

 

不少學C#的程序員一看這個就簡單地把委託認爲是C++裏面的函數指針,其實這個是錯的,委託和函數指針的區別在於,委託是有狀態的,更相似與閉包,而函數指針只是一個地址,是沒有狀態的,單純的函數指針沒法根據咱們的需求去傳入外部的狀態(也就是沒法根據需求去改變傳入參數的個數和參數的類型)

在C++裏面,咱們只能經過這樣來指向一個成員函數的指針:

int (simple:: *pfn) (int)  = simple::fn;

這樣地話咱們才能把一個類成員函數指針指向一個類的成員函數,可是這樣明顯有兩個缺點:

1.咱們須要在函數調用的時候才能傳入參數,沒法在函數肯定要指向哪個類成員函數的時候指定參數

2.最重要的一點,咱們沒法把pfn認識指向一個functionname(int)的類成員函數,咱們只能再次定義一個函數指針

 

在C++裏面實際上也是有相似於委託的功能,利用類的成員變量來保存咱們的狀態,而後重載operator(),就能夠達到咱們所說的閉包的效果

 

class Add
{
public:
    Add(int i,int j)
    {
        m_i = i;
        m_j = j;
    }

    int operator()()
    {
        return m_i + m_j;
    }

protected:
    int m_i;
    int m_j;
};

這樣咱們就能夠這樣使用這個Add類

    Add add(1,2);
    cout<<add()<<endl;

可是很明顯地,這樣作咱們每次都須要根據需求去建立一個class,實際上並無比咱們根據需求來寫函數方便地多少,還不如直接使用lambda,在C++ 11則解決了這個問題,引入了boost的function和bind

class calc
{
public:
    calc(int base)
    {
        m_base = base;
    }

    int Add(int j)
    {
        m_base += j;
        return m_base;
    }

    int sub(int j)
    {
        m_base -= j;
        return m_base;
    }
protected:
    int m_base;
};
int main(int argc,char *argv[])
{
    calc *c = new calc(10);
    tr1::function<int ()> fun;
    
    fun = tr1::bind(&calc::Add,c,3);
    cout<<fun()<<endl;

    tr1::function<int (int)> fun2;
    fun2 = tr1::bind(&calc::sub,c,tr1::_1);
    cout<<fun2(3);

}

function和bind的引入解決了咱們沒法使用C#裏面委託的問題,而且既能夠提早綁定參數,又能夠延後綁定參數,並且能夠跟任何類成員函數配合

看起來是很方便的,可是別忘記了C++裏面的一個問題,就是C++沒有內存回收的功能,若是咱們bind的類被釋放掉了,那麼會怎麼樣??

若是咱們main函數改爲下面這樣調用:

    calc *c = new calc(10);
    tr1::function<int ()> fun;
    
    
    fun = tr1::bind(&calc::Add,c,3);
    delete c;
    c = NULL;

    cout<<fun()<<endl;

那麼咱們調用fun()的時候會發生什麼事情??

咱們沒法得知計算得結果,由於此時的c已經被delete掉了,雖然能夠依然正常調用Add函數(若是對C++比較熟悉的應該知道爲何能夠正常調用Add),可是內部咱們保存的"狀態"不見了,也隨着delete動做一塊兒消失了,留下的是一串隨機的數

可是若是咱們把下面的代碼改爲這樣:

tr1::function<int ()> fun2;
{
  calc d(12);
  fun2 = tr1::bind(&calc::Add,d,40);
}

fun2();

那麼即便在d釋放掉之後,咱們依然能夠正常地調用fun2

根據個人測試,在咱們調用bind的時候,實際上將整個類的當前"狀態"都複製了過去,並且在VS2010的平臺上,我本身測試了下,調用了9次的複製構造函數,若是咱們很差好處理這個,對於性能將會是巨大的損失

當時標準庫的設計者也沒有那麼地傻,因此bind也能夠傳入指針:

fun2 = tr1::bind(&calc::Add,&d,40);

固然這個又涉及到類的狀態保存的問題,類銷燬了怎麼辦??

因此說C++的function和bind的再能模仿,也沒法模仿到C#的委託,畢竟C++裏面沒有人幫咱們管理內存,而C#程序員彷佛不用關心這些,C++程序員永遠離不開內存管理的話題

 

參考資料:

以boost::function和boost:bind取代虛函數

http://www.boost.org/doc/libs/1_54_0/doc/html/function.html

http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html

http://msdn.microsoft.com/zh-cn/library/vstudio/dd293608.aspx

相關文章
相關標籤/搜索