- 源代碼之中時而會出現一些全局函數調用操作,尤其是定義于<stl_construct.h> 之中用于對象構造與析構的基本函數,以及定義于<stl_uninitialized.h>之 中 用 于 內 存 管 理 的 基 本 函 數 , 以及定義于<stl_algobase.h>之中的各種基本算法
STL六大組件功能與運用
- 容 器 (containers) : 各種數據結構,如 ?vector, list , deque, set, map,用來存放數據,詳見本書4, 5 兩章。從實現的角度來看,STL容器是一種class template.就體積而言,這一部分很像冰山在海面下的比率
- 算 法 (algorithm s): 各種常用算法如 sort, search, copy, erase - - 詳見 第 6 章。從實現的角度來看,STL算法是一種function template.
- 迭代器(iterators): 扮演容器與算法之間的膠合劑,是所謂的“泛型指針”, 詳見第3 章.共有五種類型,以及其它衍生變化.從實現的角度來看,迭代器是一種將 operator*, operator->, operator++, operator--等指針相關操作予以重載的class template.所有STL容器都附帶有自己專屬的迭代器—— 是的,只有容器設計者才知道如何遍歷自己的元素。原生指針(native
pointer)也是一種迭代器。 - 仿函數(functors): 行為類似函數,可作為算法的某種策略(p olicy), 詳見 第7章。從實現的角度來看,仿函數是一種重載了 operator ()的class或 class template. 一般函數指針可視為狹義的仿函數。
- 配 接 器 (adapters): —種用來修飾容器(containers)或仿函數(functors)或迭代器(iterators)接口的東西,詳見第8 章? 例如,STL提供的queue和 stack,雖然看似容器,其實只能算是一種容器配接器,因為它們的底部完全 借助deque,所有操作都由底層的deque供應。改變functor接口者,稱為function adapter;改變 container 接口者,稱為 container adapter;改變 iterator
接口者,稱 為 iterator adapter.配接器的實現技術很難一言以蔽之,必須逐 —分析,詳見第8章 - 配置器(allocators): 負責空間配置與管理,詳見第2 章。從實現的角度來 看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class template.
1.8.3 SGI STL 的 編 譯 器 組 態 設 置 ( configuration )
- 不同的編譯器對C++語言的支持程度不盡相同。作為一個希望具備廣泛移植能力的程序庫,SGI S T L 準備了一個環境組態文件<stl_config.h>,其中定義了許多常量,標示某些組態的成立與否。所有STL頭文件都會直接或間接包含這個組態文件,并以條件式寫法,讓預處理器(pre-processor)根據各個常量決定取舍哪 一段程序代碼。例如:
- ?<stl_Config.h>文件起始處有一份常量定義說明,然后即針對各家不同的編 譯器以及可能的不同版本,給予常量設定。從這里我們可以一窺各家編譯器對標準C++的支持程度。
- 所謂臨時對象,就是一種無名對象(unnamed objects) >,它的出現如果不在程 序員的預期之下(例如任何pass by value操作都會引發copy操作,于是形成一 個臨時對象),往往造成效率上的負擔。但有時候刻意制造一些臨時對象,卻又是使程序干凈清爽的技巧。
- 刻意制造臨時對象的方法是,在型別名稱之后直接加一對小括號,并可指定初值,例 如 Shape (3,5)或 int(8),其意義相當于調用相應 的constructor且不指定對象名稱。
- STL 最常將此技巧應用于仿函數(functor)與 算法的搭配上,例如:
- 最后一行便是產生function template具現體print<int>的一個臨時對象。 這個對象將被傳入for_each()之中起作用。當 for_each()結束時,這個臨時對 象也就結束了它的生命。
#include <iostream>
#include <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include <functional>
#include <deque>template <typename T>
class print{
public:void operator() (const T& elem) {//operator() 重載std::cout << elem << std::endl;}
};
int main(int argc,char* argv[]){int ia[6] = {0,1,2,3,4,5};std::vector<int>iv(ia,ia+6);std::for_each(iv.begin(),iv.end(), print<int>());
}
- 靜態常量整數成員在c la ss內部直接初始化 in-class static constant integer initialization
- 如 果 class內含 const static integral data m e m b e r , 那么根據 C + + 標準規格,
我們可以在class之內直接給予初值。所 謂 integral泛指所有整數型別,不單只是 指 int。下面是一個例子:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include <functional>
#include <deque>template <typename T>
class print{
public:void operator() (const T& elem) {//operator() 重載std::cout << elem << std::endl;}
};template <typename T>
class testClass{
public:static const int _datai = 5;static const long _datal = 3L;static const char _datac = 'c';
};
int main(int argc,char* argv[]){std::cout << testClass<int>::_datai << std::endl;std::cout << testClass<int>::_datal << std::endl;std::cout << testClass<int>::_datac << std::endl;
}
- increm ent/decrem ent/dereference 操作符
- increment/dereference操作符在迭代器的實現上占有非常重要的地位,因為 任何~個迭代器都必須實現出前進(譏er e * ” 和取值(dereference, operator*) 功能,前者還分為前置式(prefix)和后置式(postfix)兩種,有非常 規律的寫法14°有些迭代器具備雙向移動功能,那么就必須再提供decrement操作 符 (也分前置式和后置式兩種)。下面是一個范例:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include <functional>
#include <deque>template <typename T>
class print{
public:void operator() (const T& elem) {//operator() 重載std::cout << elem << std::endl;}
};template <typename T>
class testClass{
public:static const int _datai = 5;static const long _datal = 3L;static const char _datac = 'c';
};class INT{friend std::ostream& operator<<(std::ostream & os,const INT& i);
public:INT(int i) : m_i(i){};//prefix : increment and then fetchINT& operator++(){++(this->m_i); //隨著class的不同,此行應該有不同的操作return *this;}//postfix : fetch and then incrementconst INT operator++(int){INT temp = *this;++(*this);return temp;}//postfix : decrement and then fetchINT& operator--(){--(this->m_i);return *this;}//postfix : fetch and then decrementconst INT operator--(int){INT temp = *this;--(*this);return temp;}//dereferenceint& operator*() const{return (int&)m_i;//以上轉換操作告訴編譯器,你確實要將const int轉為non-const lvalue.//如果沒有這樣明白地轉型,有些編譯器會給你警告,有些更嚴格的編譯器會視為錯誤}private:int m_i;
};std::ostream& operator<<(std::ostream&os,const INT& i){os << '[' << i.m_i << ']';return os;
}int main(int argc,char* argv[]){INT I(5);std::cout << I++;std::cout << ++I;std::cout << I--;std::cout << --I;std::cout << *I;
}
- 前 閉 后 開 區 間 表 示 法 [)
- 任何一個STL算法,都需要獲得由一對迭代器(泛型指針)所標示的區間,用以表示操作范圍。這一對迭代器所標示的是個所謂的前閉后開區間15,以 [first, last)表示。也就是說,整個實際范圍從first開始,直到last-1.迭代器last 所指的是“最后一個元素的下一位置”。這種off by one (偏移一格,或說pass the end)的標示法,帶來了許多方便,例如下面兩個STL算法的循環設計,就顯得干凈利落:
- 因為 以下兩個函數都是遞增遍歷元素,所以使用 InputIterator
template <class InputIterator,class T>
InputIterator find(InputIterator first,InputIterator last,const T& value){while(first != last && *first!= value){return first;}
}template <class InputIterator,class Function>
Function for_each(InputIterator first,InputIterator last,Function f){for (; first != last;++first) {f(*first);}return f;
}
- ?function call 操 作 符 ( o p e r a to r 。)
- 很少有人注意到,函數調用操作(C ++語法中的左右小括號)也可以被重載
- 許多STL算法都提供了兩個版本,一個用于一般狀況(例如排序時以遞增方式排列),一個用于特殊狀況(例如排序時由使用者指定以何種特殊關系進行排列)。像這種情況,需要用戶指定某個條件或某個策略,而條件或策略的背后由一整組操作構成,便需要某種特殊的東西來代表這“一整組操作”
- 代表“一整組操作”的,當然是函數? 過去C 語言時代,欲將函數當做參數傳遞 ,唯有通過函數指針(pointer to function,或 稱 function pointer)才能達成,例如:
- ?但是函數指針有缺點,最重要的是它無法持有自己的狀態(所謂局部狀態,local states), 也無法達到組件技術中的可適配性(adaptability)—— 也就是無法再將某 些修飾條件加諸于其上而改變其狀態。
- 為此,S T L 算法的特殊版本所接受的所謂“條件”或 “策略”或 “一整組操作”,都以仿函數形式呈現。所謂仿函數(functor)就是使用起來像函數一樣的東 西。如果你針對某個class進 行 operator()重載,它就成為一個仿函數。至于要 成為一個可配接的仿函數,還需要做一些額外的努力(詳見第8 章 )。
- 上 述 的 plus<T>和 minus<T>已經非常接近STL的實現了,唯一的差別在 于 它 缺 乏 “可配接能力”。關 于 “可配接能力
template <class T>
struct plus{T operator() (const T&x,const T&y) const{return x+y;}
};template <class T>
struct minus{T operator()(const T&x,const T&y) const{return x-y;}
};int main(int argc,char* argv[]){plus<int>plus_obj{};minus<int>minus_obj{};//以下使用仿函數,就像使用一般函數一樣std::cout << plus_obj(3,5) << std::endl;std::cout << minus_obj(3,5) << std::endl;//以下直接產生仿函數的臨時對象(第一對小括號),并調用之(第二對小括號)std::cout << plus<int>()(3,5) << std::endl;std::cout << minus<int>()(5,3) << std::endl;
}
請使用手機"掃一掃"x