程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 使用BOOST BIND庫提高C++程序性能

使用BOOST BIND庫提高C++程序性能

編輯:C++入門知識

 

 

  • By Björn Karlsson
  • Aug 26, 2005

     

    翻譯:Boost.Bind的用法

    Boost.Bind為函數和函數對象,值語義和指針提供語義了一致的語法。我們首先通過一些簡單的例子來看看它的基本用法,之後我們會延伸到嵌套綁定以實現功能組合。理解bind用法的一個關鍵是理解占位符(placeholder)的概念。占位符表示該參數將在函數對象裡面提供。Boost.Bind提供多達9個這樣的參數——_1, _2, _3, _4, _5,_6,_7,_8, _9。你可以在想要加入參數的地方使用它們。在第一個示例程序中,我們定義一個函數“nine_arguments”,之後用bind表達式調用它。

     

    #include 
    #include boost/bind.hpp
    
    void nine_arguments(
      int i1,int i2,int i3,int i4,
        int i5,int i6,int i7,int i8, int i9) {
        std::cout << i1 << i2 << i3 << i4 << i5
          << i6 << i7 << i8 << i9 << '
    ';
    }
    
    int main() {
      int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
      (boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7))
        (i1,i2,i3,i4,i5,i6,i7,i8,i9);
    }

    在這個程序中,你可以創建臨時的匿名綁定器並且立即傳參調用。如你所見,占位符的順序是在本例中是混亂的,這使得參數的順序也被打亂。另外,占位符可以在表達式中重復使用。示例程序1的輸出是:

     

     

    921638457

    占位符的序數與參數的位置是對應的,也就是說,_1被換為第一個參數,_2被換為第二個參數,以此類推。

     

     

    原創:編程實驗

     

    #include 
    #include 
    
    void PlaceholderTest(int a, int b, int c)
    {
    	std::cout << a << b << c << std::endl;
    }
    
    int main()
    {
    	int a = 1, b = 2, c = 3;
    	//(boost::bind(&PlaceholderTest, _1, _2))(a, b);	//error
    	//(boost::bind(&PlaceholderTest, _1, _2, _1, _3))(a, b, c);	//error
    	//(boost::bind(&PlaceholderTest, _1, _2, _3))(a, b);	//error
    	(boost::bind(&PlaceholderTest, _1, _2, _1))(a, b);	//OK,output:121
    	(boost::bind(&PlaceholderTest, 99, _2, _1))(a, b)	//OK,output:9921
    } 

    總結:

     

    1、參數占位符數量與函數形參數量必須一致

    2、占位符的替代值可以少於占位符數量

    3、占位符和實參可以混合使用

     

    翻譯:調用成員函數(1)

    我們看看如何使用bind調用類的成員函數。首先我們也從一個可以由標准庫完成的操作開始,這樣方便我們對比標准庫調用和Boost.Bind調用。當我們在標准庫容器類類型中存儲元素時,通常需要對部分或所有元素調用成員函數。通常的實現方法是,將這些操作可以放在一個循環中。但是現在有更好的解決辦法。觀察如下的簡單類——status。我們之後將用它來展示Boost.Bind的簡單易用和強大之處。

     

    class status {
    	std::string name_;
    	bool ok_;
    public:
    	status(const std::string& name):name_(name),ok_(true) {}
    
    	void break_it() {
    		ok_=false;
    	}
    
    	bool is_broken() const {
    		return ok_;
    	}
    
    	void report() const {
    		std::cout << name_ <<  is  <<
    			(ok_ ? working nominally:terribly broken) << '
    ';
    	}
    };

    如果我們將這個類的實例儲存在vector中,當我們需要調用成員函數report時,大概要遵循以下步驟

     

     

    std::vector statuses;
    statuses.push_back(status(status 1));
    statuses.push_back(status(status 2));
    statuses.push_back(status(status 3));
    statuses.push_back(status(status 4));
    
    statuses[1].break_it();
    statuses[2].break_it();
    
    for (std::vector::iterator it=statuses.begin();
           it!=statuses.end();++it) {
      it->report();
    }

    for循環能夠正確完成操作,但是它冗長、低效(每次都要檢查statuses.end()),還不如使用標准庫中專為這種操作設計的for_each算法來的清楚。為了使用for_each替代for循環,我們需要為vector元素調用成員函數report配置一個適配器。在這個實例中,由於元素是值存儲的,我們需要的是mem_fun_ref適配器:

     

     

    std::for_each(
    	statuses.begin(),
    	statuses.end(),
    	std::mem_fun_ref(&status::report));

    這是一種更好的辦法——它是如此簡潔,不會對代碼的作用產生任何迷惑和誤解。Boost.Bind中的等效代碼如下:

     

     

    std::for_each(
    	statuses.begin(),
    	statuses.end(),
    	boost::bind(&status::report,_1));

    bind版本仍然清晰明了。這是我們第一次真正使用上面提及的Bind庫占位符,它向編譯器和代碼閱讀者傳遞了這樣一個信息,_1將在調用綁定器的函數中被實參替換。盡管這段代碼長度減少了,但在本例中,它與使用標准庫mem_fun_ref幾乎沒有差別。

     

    原創:編程實驗

     

    #include 
    #include 
    #include 
    #include 
    
    #include 
    
    class Status
    {
    public:
    	Status(const std::string &name) : name_(name), ok_(true){}
    
    	void BreakIt()
    	{
    		ok_ = false;
    	}
    
    	bool IsBroken() const
    	{
    		return ok_;
    	}
    
    	void Report() const
    	{
    		std::cout << name_ <<  is  << (ok_ ? ok : broken) << std::endl;
    	}
    
    private:
    	std::string name_;
    	bool ok_;
    };
    
    int main()
    {
    	std::vector v_status;
    
    	
    	v_status.push_back(Status(status 1));
    	v_status.push_back(Status(status 2));
    	v_status.push_back(Status(status 3));
    	v_status.push_back(Status(status 4));
    
    	v_status[1].BreakIt();
    	v_status[2].BreakIt();
    
    	std::cout << use or: << std::endl;
    	for (std::vector::iterator it = v_status.begin(); it < v_status.end(); it++)
    	{
    		it->Report();
    	}
    
    	std::cout << 
    use or_each, mem_fun_ref: << std::endl;
    	std::for_each(v_status.begin(), v_status.end(), std::mem_fun_ref(&Status::Report));
    
    	std::cout << 
    use or_each, bind: << std::endl;
    	//std::for_each(v_status.begin(), v_status.end(), boost::bind(&Status::Report));	//error
    	std::for_each(v_status.begin(), v_status.end(), boost::bind(&Status::Report, _1));
    }

    總結:

     

    bind成員函數最大的不同是,必須指明調用該函數的實例對象,代碼中用“_1”表示。

    翻譯:調用成員函數(2)

    下面,我們稍微改造一下vector容器,讓它裝入指針而不是值:

     

    std::vector p_statuses;
    p_statuses.push_back(new status(status 1));
    p_statuses.push_back(new status(status 2));
    p_statuses.push_back(new status(status 3));
    p_statuses.push_back(new status(status 4));
    
    p_statuses[1]->break_it();
    p_statuses[2]->break_it();

    我們仍然可以用兩種標准庫,但是我們不能用mem_fun_ref,而是用mem_fun適配器,雖然它的名字聽起來有點兒混淆,完成操作還是沒問題的。

     

    std::for_each(
    	p_statuses.begin(),
    	p_statuses.end(),
    	std::mem_fun(&status::report));

    注意到,這段代碼的語法已經有所改變,盡管我們要做的工作幾乎相同。當然,如果代碼的語法和上面的例子一樣就最好了,這樣我們就可以更多地關注代碼到底做了些什麼而不是它怎麼做的。使用Bind,我們不需要顯式指明要處理的元素是指針(這在容器類型中已經說明了,重復的信息在現代的庫裡面顯然是不必要的)

     

     

    std::for_each(
    	p_statuses.begin(),
    	p_statuses.end(),
    	boost::bind(&status::report,_1));

    如你所見,這與之前非指針元素的代碼沒有任何區別。也就是說如果你理解了剛才的bind,那麼這個也能理解。

     

    原創:編程實驗

     

    #include 
    #include 
    #include 
    #include 
    
    #include 
    
    class Status
    {
    public:
    	Status(const std::string &name) : name_(name), ok_(true){}
    
    	void BreakIt()
    	{
    		ok_ = false;
    	}
    
    	bool IsBroken() const
    	{
    		return ok_;
    	}
    
    	void Report() const
    	{
    		std::cout << name_ <<  is  << (ok_ ? ok : broken) << std::endl;
    	}
    
    private:
    	std::string name_;
    	bool ok_;
    };
    
    int main()
    {
    	std::vector v_pstatus;
    	
    	v_pstatus.push_back(new Status(status 1));
    	v_pstatus.push_back(new Status(status 2));
    	v_pstatus.push_back(new Status(status 3));
    	v_pstatus.push_back(new Status(status 4));
    
    	v_pstatus[1]->BreakIt();
    	v_pstatus[2]->BreakIt();
    
    	std::cout << use or: << std::endl;
    	for (std::vector::iterator it = v_pstatus.begin(); it < v_pstatus.end(); it++)
    	{
    		(*it)->Report();
    	}
    
    	std::cout << 
    use or_each, mem_fun_ref: << std::endl;
    	std::for_each(v_pstatus.begin(), v_pstatus.end(), std::mem_fun(&Status::Report));
    
    	std::cout << 
    use or_each, bind: << std::endl;
    
    	std::for_each(v_pstatus.begin(), v_pstatus.end(), boost::bind(&Status::Report, _1));
    }

    總結:

     

    只有bind保持了形式不變

     

    翻譯:調用成員函數(3)

    我們決定使用指針後,有另外一個問題,即指針的生命周期控制。我們必須手動釋放p_statuses中的元素,這很容易出錯而且沒有必要。因此,我們可能選擇使用智能指針(smart pointers),代碼變化如下:

     

    std::vector > s_statuses;
    s_statuses.push_back(
    	boost::shared_ptr(new status(status 1)));
    s_statuses.push_back(
    	boost::shared_ptr(new status(status 2)));
    s_statuses.push_back(
    	boost::shared_ptr(new status(status 3)));
    s_statuses.push_back(
    	boost::shared_ptr(new status(status 4)));
    s_statuses[1]->break_it();
    s_statuses[2]->break_it();

    現在,我們該使用標准庫中的哪個適配器了?由於智能指針並沒有report成員函數,mem_fun和mem_fun_ref都不能用了。如下代碼會編譯失敗。

     

     

    std::for_each(
    	s_statuses.begin(),
    	s_statuses.end(),
    	std::mem_fun(&status::report));

    我們的好運用完了,標准庫並不能幫我們完成這個任務。因此,我們只能借助於之前想避開的for形式或者……Boost.Bind,它可以完全正確地完成任務。

     

     

    std::for_each(
    	s_statuses.begin(),
    	s_statuses.end(),
    	boost::bind(&status::report,_1));

    這與前面的代碼是完全一樣的(除了容器的名字)。同樣的語法可以用於綁定值語義、指針語義或者只能指針。有時候,不同的語法可以幫助我們理解代碼,但在我們討論的情況中不是這樣——我們手中的任務是在容器的元素上調用成員函數,沒有其他的需求。語法一致的價值是不容輕視的,它既幫助了寫代碼的人,也幫助了以後維護代碼的人。(當然,實際上我們沒有寫需要維護的代碼,但出於參數的考慮,讓我們假裝是這樣做的吧^_^)。

     

    原創:編程實驗

     

     

    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    class Status
    {
    public:
    	Status(const std::string &name) : name_(name), ok_(true){}
    
    	void BreakIt()
    	{
    		ok_ = false;
    	}
    
    	bool IsBroken() const
    	{
    		return ok_;
    	}
    
    	void Report() const
    	{
    		std::cout << name_ <<  is  << (ok_ ? ok : broken) << std::endl;
    	}
    
    private:
    	std::string name_;
    	bool ok_;
    };
    
    int main()
    {
    	std::vector< boost::shared_ptr > v_spstatus;
    	
    	v_spstatus.push_back(boost::shared_ptr(new Status(status 1)));
    	v_spstatus.push_back(boost::shared_ptr(new Status(status 2)));
    	v_spstatus.push_back(boost::shared_ptr(new Status(status 3)));
    	v_spstatus.push_back(boost::shared_ptr(new Status(status 4)));
    
    	v_spstatus[1]->BreakIt();
    	v_spstatus[2]->BreakIt();
    
    	//std::cout << use or: << std::endl;
    	//for (std::vector::iterator it = v_spstatus.begin(); it < v_spstatus.end(); it++)
    	//{
    	//	(*it)->Report();
    	//}
    
    	//std::cout << 
    use or_each, mem_fun_ref: << std::endl;
    	//std::for_each(v_spstatus.begin(), v_spstatus.end(), std::mem_fun(&Status::Report));
    
    	std::cout << 
    use or_each, bind: << std::endl;
    	std::for_each(v_spstatus.begin(), v_spstatus.end(), boost::bind(&Status::Report, _1));
    }

    總結:

     

    1、標准庫中的方法不再能夠使用

    2、使用share_ptr必須引入頭文件#include

     

    這些例子展示了一些Boost.Bind最基本最常用的情況,也是它最擅長的方面。盡管標准庫也提供了一些基本工具讓我們完成同樣的任務,但我們看到Bind提供了語法一致性和一些標准庫目前沒有的擴展功能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved