[GeekBand] STL與泛型編程-1

迭代器(iterator)
C++中的類模板(class template)與函數(shù)模板(funtion template)可以分別獨立完成數(shù)據(jù)容器(containers)和算法(algorithms)的設計,這樣就分別實現(xiàn)了容器與算法的泛型化,而這兩者之間的連接起來則依靠迭代器(iterators)實現(xiàn),因此一種算法可以通過迭代器的粘合而作用到不同的容器上。以下為應用迭代器的簡單示例:

template <class InputIterator, class T>
InputIterator find(InputIterator first,
                           InputIterator last,
                           const T& value) {
    while (first != last && *first != value) {
        first++;
    }
    return first;
}

每種容器都有其對應的迭代器,下面定義的三種容器均有自身對應的迭代器,上述find 算法通過迭代器而能適用于不同的容器。

std::vector<int> v(...)
std::list<int> l(...)
std::deque<int> d(...)
...
std::vector<int>::iterator itv = find(v.begin(), v.end(), value)
std::list<int>::iterator itl = find(l.begin(), l.end(), value)
std::deque<int>::iterator itd = find(d.begin(), d.end(), value)

迭代器是一種行為類似指針的對象,迭代器這種類里面對 operator* 和 operator->進行了重載(overloading),因此可以像指針一樣進行內容提領(deference)和成員訪問(member access)這兩項工作。

特性(traits)
算法中運用迭代器時,可能會用到其關聯(lián)型別(associated type),包括以下五種:

  • value_type
  • difference_type
  • pointer
  • reference
  • iterator_category

其中 value_type 是迭代器所指對象的型別。假設算法中需要聲明一個以 value_type 為型別的變量,由于沒有C++不支持 typeof(),且 RTTI 中的 typeid()只能獲取型別名稱,不能用于變量聲明,所以需要采取特定的解決方法。第一種可供考慮的解決方案就是利用函數(shù)模板的參數(shù)推導(argument deducation)機制 ,程序示例如下:

template <class I, class T>
void func_impl(I iter, T t) {
    T tmp;    // T 就是迭代器所指對象的型別,以此解決了上述問題
    ...    // 這里做原本 func( ) 應該做的工作   
}

template <class I>
inline
void func(I iter) {
    func_impl(iter, *iter);
}

int main() {
    int i;
    func(&i);
}

如果需要以 value_type 作為函數(shù)返回值,則上述函數(shù)的參數(shù)推導機制將失效,此時可考慮聲明內嵌型別來解決:

template <class T>
struct MyIter {
    typedef T value_type;    // 內嵌型別聲明(nested type)
    T* ptr;
    MyIter(T* p = 0) : ptr(p) { }
    T& operator*() const { return *ptr; }
    ...
};

template <class I>
typename I::value_type    // 用 typename 聲明 func 的返回值型別
func(I ite) {
    return *ite;
}
...
MyIter<int> ite(new int(8));
cout << func(ite);    // 輸出:8

當?shù)鞑皇?class type,而是原生指針時,上述方法再次失效。此時可考慮用一個專門的 class template 來萃取迭代器的特性,并且以模板偏特化來應對獲取原生指針的型別的問題:

template <class I>
struct iterator_traits {
    typedef typename I::value_type value_type;
};

應對原生指針的特化版本如下:

template <class T>
struct iterator_traits<T*> {
    typedef T value_type;
};

運用模板類 iterator_traits 之后,函數(shù) func 可以寫成如下形式:

template <class I>
typename iterator_traits<I>::value_type
func(I ite) {
    return *ite;
}

Vector
Vector 是能夠存放任意型別的動態(tài)數(shù)組,在內存中是一段地址連續(xù)的空間,支持動態(tài)空間大小調整,隨著元素的加入,vector 內部會自動擴充內存空間。

  1. 創(chuàng)建 Vector
std::vector<T> v;
std::vector<T> v(n);    // 容量為 n
std::vector<T> v(n, i)    // 容量為 n,均初始化為 i
std::vector<T> copyOfV(v)
int array[] = {1,2,3,4,5,6,7,8,9,10}; std::vector<T> v(array, array + 10);
  1. 向 Vector 中添加元素
std::vector<std::wstring> v3;
for (std::size_t i = 0; i < 10; i++) {
std::wstringstream wss;
wss << TEXT("String[") << i << TEXT("]");
v3.push_back(wss.str());
}
  1. 判斷 Vector 是否為空
std::vector<std::wstring> v3;
bool isEmpty = v3.empty();
  1. 獲取 Vector 的大小
int array[] = {1,2,3,4,5,6,7,8,9,10};
std::vector<int> v(array, array + 10);
std::size vSize = v.size();
  1. 訪問 Vector 中元素
  • 調用 vector::at(),進行邊界檢查
  • 調用 vector::operator[],不進行邊界檢查
  1. 從 Vector 中刪除元素
    • 清除整個 vector:clear
  • 彈出 vector 尾部元素:pop_back
  • 刪除 vector 中某一位置元素:erase
// 指定 iterator 處刪除某一元素
std::vector<int>::const_iterator it = v.begin();
v.erase(it + 1);  //刪除第二個元素
// 通過某一條件函數(shù)找到 vector 中需要刪除的元素
struct ContainString: public std::unary_function<std::wstring, bool> {
    ContainString(const std::wstring& wszMatch): m_wszMatch(wszMatch) { }
    bool operator() (const std::wstring& wszStringToMatch) const {
        return (wszMatchToMatch.find(m_wszString) != -1);
    }
    std::wstring m_wszString;
}
v.erase(std::remove_if(v.begin(), v.end(), ContainsString(L"C++")), v.end());

Deque
Deque 是一個能存放任意型別的雙向隊列,其提供的函數(shù)與 vector 類似,采用了與 vector 不同的內存管理方式:大塊分配內存。Deque 新增了兩個函數(shù):

  • 在頭部插入元素:push_front
  • 在尾部彈出元素:pop_front
  1. 中間位置插入元素
    dqInt.insert(dqInt.begin() + 1, 9);  // 在第二個元素之前插入9
    
  2. 前向遍歷與反向遍歷
// 前向遍歷
deque<int>::iterator i, iend;
iend = dqInt.end();
for (i = dqInt.begin(); i != iend; ++i) {
    cout << *i << " ";
}
cout << endl;
// 反向遍歷
deque<int>::reverse_iterator ri, riend;
riend = dqInt.rend();
for(ri = dqInt.rbegin(); ri != riend; ++ri) {
    cout << *ri < " ";
}
cout << endl;

List
List 是一個能存放任意型別的雙向鏈表(doubly linked list)。

  • 可以向鏈表中接入一個子鏈表(sub-list)
  • 優(yōu)點:
    1. 不使用連續(xù)內存完成動態(tài)操作;
    2. 對于插入、刪除和替換等需要重排序列的操作,效率極高;
    3. 可在兩端隨意進行插入、刪除操作;
  • 缺點:
    1. 不能進行內部的隨機訪問,即不像 vector 那樣支持 operator [] 和vector.at();
    2. 相對于verctor占用內存多。
  1. 創(chuàng)建 list
std::list<int> l;
std::list<int> l(n);  // 容量為 n
std::list<int> l(n, x);  // 容量為 n,均初始化為 x
std::list<int> copyOfList(l);

std::wstring array[] = {TEXT("Str-1"), TEXT("Str-2"), TEXT("Str-3")};
std::list<std::wstring> l(array, array+3);
  1. 向 list 添加元素
std::list<std::wstring> l;
l.push_back(TEXT("Some text pushed at back"));
l.push_front(TEXT("Some text pushed at front"));
  1. 判斷 list 是否為空
std::list<std::wstring> l;
bool isEmpty = l.empty();
  1. 獲取 list 大小
std::wstring array[] = {TEXT("Str-1"), TEXT("Str-2"), TEXT("Str-3")};
std::list<std::wstring> l(array, array+3);
std::size listSize = l.size();
  1. 刪除 list 中元素
  • 清除整個 list:clear(),內部調用 erase(begin(), end())
  • 彈出 list 尾部元素:pop_back()
  • 彈出 list 頭部元素:pop_front()
  • 刪除 list 中指定元素:erase(), remove(),remove_if()
  1. 向 list 中插入元素
std::list<std::wstring>::const_iterator it = l.begin();
l.insert(it, anotherList.begin(), anotherList.end());
  1. 粘接 list:splice
// position為目標 list 位置,x 為源 list,first 與 last 限定源的范圍
void splice ( iterator position, list<T, Allocator>& x ); 
void splice ( iterator position, list<T, Allocator>& x, iterator i );
void splice ( iterator position, list<T, Allocator>& x, 
                 iterator first, iterator last );
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容