適配器也是一種常用的設計模式: 將一個類的接口轉換為另一個類的接口,使得原本因接口不兼容而不能合作的兩個類可以一起運作。STL提供三種適配器:改變容器接口的容器適配器、改變迭代器接口的迭代器適配器以及改變仿函數接口的仿函數適配器。前兩者都較為簡單,而最後一種則是靈活性最大的,有了它我們可以構造非常復雜的表達式策略。
容器適配器常見的是stack和queue,他們的底層存儲都是用deque完成的,再在deque上封裝一層接口以滿足stack和queue的要求。
迭代器適配器大致有三種對應不同的迭代器行為,它們以某容器為參數,直接對容器的迭代器進行封裝,主要有back_insert_iterator、front_insert_iterator、insert_iterator以及reverse_iterator。
從上面兩個適配器看,其原理都是在其內部有一個原來要適配的成員變量,通過改變接口來實現,那麼仿函數的適配器也不例外。常用的是bind1st, bind2nd, not1, compose1, compose2等等,這些適配器都是仿函數同時以要適配的仿函數作為member object。仿函數適配器的實現主要包括兩塊,自身的類以及方便使用的函數,以bind1st為例,它的作用是綁定二元仿函數的第一個參數為某指定值。首先是其定義:
1 //從它繼承自unary_function即可得知它也是仿函數 2 template <class Operation> 3 class binder1st: public unary_function<typename Operation::second_argument_type, 4 typename Operation::result_type> 5 { 6 protected: 7 Operation op; //以要適配的仿函數為成員變量 8 typename Operation::first_argument_type value; //第一個參數 9 public: 10 binder1st(const Operation& x,const typename Operation::first_argument_type& y) 11 : op(x), value(y) {} //構造函數裡對兩個成員變量賦值 12 typename Operation::result_type 13 operator()(const typename Operation::second_argument_type& x) const { 14 return op(value, x); //重載並接受第二個參數,以完成適配 15 } 16 };
仿函數適配器第二個部分是方便使用的函數,以讓我們可以像普通函數一樣使用適配器,並通過函數模板的參數推導功能來創建適配器對象。
1 template <class Operation, class T> 2 inline binder1st<Operation> bind1st(const Operation& op, const T& x) { 3 typedef typename Operation::first_argument_type arg1_type; 4 return binder1st<Operation>(op, arg1_type(x));//返回對象 5 }
適配器很巧妙的構造了這樣一種對象嵌套對象的結構來使得我們可以構造很復雜的語義,這也是函數指針所不具備的,當然對於函數指針STL也提供了ptr_fun來將其變為函數對象以獲得適配功能,成員函數得使用mem_fun,mem_fun_ref。