天天看點

擴充卡模式—STL中的擴充卡模式分析

擴充卡模式通常用于将一個類的接口轉換為客戶需要的另外一個接口,通過使用Adapter模式能夠使得原本接口不相容而不能一起工作的類可以一起工作。

這裡将通過分析c++的标準模闆庫(STL)中的擴充卡來學習adapter模式。由于STL中的擴充卡過多,不可能全部都具體介紹,所有這裡将簡單介紹STL中存在的擴充卡,但将通過具體分析STL中istream_iterator擴充卡來分析adapter模式。

1. STL中的擴充卡

在STL中廣泛使用了Adapter模式,主要有container adapter、iterator adapter、functor adapter

  • container adapter: stack, queue 資料結構
  • iterator adapter: front_insert_iterator, back_insert_iterator, istream_iteator, ostream_iterator
  • functor adapter: bind, negate, compose 與對一般函數或者成員函數的修飾

2. Container Adapters

在STL中,stack、queue、priority queue是通過借助deque的所提供的接口來實作。

假設客戶(希望使用stack、queue、priority queue的程式員)希望能夠使用stack資料結構,則需要提供一個能夠實作stack功能的類給客戶,該類應該提供能夠實作stack的push與pop操作的接口給客戶調用,并且該類中不應該含有其他與stack能夠提供的功能無關的接口。但是,當程式中不存在這樣的類時,則可以通過借助已經存在的類deque,定義一個stack類,使用deque所提供的接口來實作stack類應該提供的接口,即上文所述—将一個類的接口轉換為客戶需要的另外一個接口。

3. Iterator Adapters

疊代器本身是一種模式,疊代器模式使得客戶可以抽象地看待各種資料容器,即将所有的資料容器(vector, list, deque等)看做一個資料流。流中的每個元素為一個資料(可以為各種類型的資料),通過對疊代器執行自增與自減操作,可以周遊流中的每一個元素。這樣,無論一個資料結構是如何實作的,都可以将其看做一個流,如同數組一樣,通過自增自減即可擷取下一個元素或者上一個元素。

類似的從抽象角度看待資料容器的觀點,使我們想起Unix中将檔案、套接字等都統一看做檔案來進行處理的觀點,檔案與套接字都支援從打開、關閉、讀取資料、輸入資料等操作,但是具體的實作細節不同,對于熟悉面向對象的人而言,能夠立即想到面向對象中的多态,接口相同而具體行為不同。

在如今,通過對檔案、套接字進一步抽象,将檔案、套接字等具有輸入與輸出功能的物體抽象為輸入與輸出流,如在Java中的位元組流、檔案流、套接字等。我們同樣可以周遊輸入輸出流中的每個元素,從這個角度講,從輸入流中讀取資料就如同從容器中讀取資料一樣,向輸出流中輸入資料則如同向容器中增加元素一樣,則在STL中能夠适用于容器的算法應該也需要能夠使用與輸入輸出流。istream_iterator與ostream_iterator即是為了能夠讓适用于容器的算法同樣也能夠适配于輸入輸出流而出現的。

void IstreamIteratorTest( )
{
  const int size = 10;
  auto print = [](int x) { std::cout << x << std::endl; };

  std::vector<int> vec;
  for (int i = 0; i < size; ++i)
    vec.push_back(i);
  std::vector<int> copy_from_container(size);
  std::copy(vec.begin( ), vec.end( ), copy_from_container.begin());
  std::for_each(copy_from_container.begin( ), copy_from_container.end( ), print);

  std::vector<int> copy_from_istream(size);
  std::istream_iterator<int> int_istream(std::cin), eos;
  std::copy(int_istream, eos, copy_from_istream.begin());
  std::for_each(copy_from_istream.begin( ), copy_from_istream.end( ), print);
}                

在該程式中,copy_from_container拷貝vec容器中的元素,copy_from_istram通過從輸入流中讀取資料來拷貝到容器中,這裡可以将容器vec和輸入流都看做可以不斷讀取資料元素的流,通過将在流中讀取的每一個元素都複制到copy_from_container或者copy_from_istream中,也可以看做讀取到copy_from_container或者copy_from_istream輸出流中,重點是這裡使用了相同的算法copy,copy算法本來适用于容器的疊代器,通過對疊代器的自增、自減來周遊擷取容器中的元素,而istream與ostream并不支援copy算法中對疊代器的取值與自增、自減等操作,通過使用Adapter模式,使能夠使用與容器的算法同樣也能夠使用與輸入與輸出流,即—使用Adapter模式能夠使得原本接口不相容而不能一起工作的類可以一起工作。

// copy algorithm
template <typename InputIterator, typename OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
                           OutputIterator result, input_iterator_tag)
{
  for (; first != last; ++first, ++result)
    *result = *first;
  return result;
}                

這裡給出istream_iterator的實作:

template <typename T, typename Distance> class istream_iterator;

template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y);

template <typename T, typename Distance = ptrdiff_t>
class istream_iterator {
  friend bool operator==<>(const istream_iterator<T>& x, const istream_iterator<T>& y);

public:
  typedef input_iterator_tag iterator_category;
  typedef T                  value_type;
  typedef const T*           pointer;
  typedef const T&           reference;
  typedef Distance           difference_type;

  istream_iterator( ) : stream(&std::cin), end_marker(false) { }
  istream_iterator(std::istream& s) : stream(&s), end_marker(false) { read( ); }
  reference operator*( ) const { return value; }
  pointer operator->( ) const { return &(operator*( )); }
  bool operator!=(const istream_iterator<T, Distance>& rhs)
  {
    return !(*this == rhs);
  }

  istream_iterator<T, Distance>& operator++( )
  {
    read( );
    return *this;
  }

  istream_iterator<T, Distance>& operator++(int)
  {
    istream_iterator<T, Distance> tmp = *this;
    read( );
    return tmp;
  }

protected:
  std::istream* stream;
  T value;
  bool end_marker;

  void read( )
  {
    end_marker = (*stream) ? true : false;
    if (end_marker)
      *stream >> value;
    end_marker = (*stream) ? true : false;
  }

  bool equal(const istream_iterator& x) const
  {
    return (end_marker == x.end_marker) && (!end_marker || stream == x.stream);
  }

};

template <typename T, typename Distance>
bool operator==(const istream_iterator<T, Distance>& x, const istream_iterator<T, Distance>& y)
{
  return x.equal(y);
}                

front_insert_iterator、back_insert_iterator與insert_iterator的出現是因為在某些情況下需要将對疊代器的指派操作轉換為push操作或者insert操作,不同之處在于插入的元素位置不同。如copy算法是将一個疊代器所指的資料拷貝到另一個疊代器所指的位置上,而如果想在該位置插入元素,而并不想對原來的該位置上的元素值進行更改,則需要使用使用插入操作替換指派操作,這裡就可以更加插入的位置不同而選擇相應的front_insert_iterator、back_insert_iterator或者insert_iterator。

4. Functor Adapters

仿函數擴充卡主要用來修飾函數,為函數添加某些需要的功能,在函數式語言如Scheme中可以通過高階函數實作。在c++中可以通過将每個函數實作為一個class來模拟這種行為。如通過函數g(x) = 3 * x, f(x) = x + 2構造出函數t(x) = f(g(x)),則可以使用Functor Adaptors來實作,下面給出Scheme與c++的實作方法,Java的實作方法與c++類似。

Scheme實作:

(define (func-t func-f func-g)
  (lambda (x)
    (func-f (func-g x))))
(define (func-f x)
  (+ x 2))
(define (func-g x)
  (* x 3))

(print ((func-t func-f func-g) 3))
           

c++實作:

template <typename Func1, typename Func2>
class T {
private:
  Func1 func1_;
  Func2 func2_;

public:
  T(Func1 func1, Func2 func2)
    : func1_(func1), func2_(func2)
  { }

  int operator()(int x)
  {
    return func1_(func2_(x));
  }
};


void FunctorTest( )
{
  auto func1 = [](int x) { return x + 2; };
  auto func2 = [](int x) { return x * 3; };
  std::cout << T<decltype(func1), decltype(func2)>(func1, func2)(3) << std::endl;
}                

轉載于:https://www.cnblogs.com/tallisHe/p/5486740.html