前言 本章是《STL源码剖析》的第一章内容摘要和读书笔记,主要是介绍STL的发展历史和几个不同的版本。
读完本章需要了解的是STL六大组件之间的关系。
STL概论 STL低层次来说是一个组件库,方便使用;高层次来说是一个抽象概念库,定义了一些概念应该具有的标准。
STL六大组件 功能和运用
STL源码就在C++的头文件中,一般存在两个版本,具有扩展名(为了向下兼容)和不具有扩展名(C++建议的命名方式)。GNU C++的SGI版本不但有一线的<vector.h>和<vector>,还有二线的<stl_vector.h>
GNU源码开放精神
实际上是一种代码前的声明,即允许使用,但是条件是加上这个声明。
由于出现了很多不同的开放源码分支,为了整合各方,后面又出现了open source名字,只要符合9条定义就是open source软件。
GCC全称是GNU Compiler Collection,是由GNU开发的编程语言编译器。
HP实现版本 STL最早的版本是HP实现版本,需要遵守的是在所有文文件加上HP的版本声明,这种授权不属于GNU GPL,属于open source。
P.J.Plauger实现版本 PJ版本继承HP版本,但是还加上了自己的声明,因此既不属于open source,也不属于GNU GPL。
被Visual C++采用。由于Visual C++对于C++的语言特性支持并不理想,所以PJ版本变现受到影响。
Rouge Wave实现版本 由Rouge Wave公司开发,继承HP版本,加上自己的版权声明。因此既不属于open source,也不属于GNU GPL。
由C++ Bulider采用。
STLport实现版本
SGI STL实现版本 SGI版本由Silicon Graphics Computer System,Inc.公司发展,继承HP版本,属于open source的一员,但是不属于GNU GPL。
SGI版本被GCC采用,可以在GCC的include字幕下找到所有的STL文件。
GNU C++头文件分布 众多头文件大致可以分为:
SGI STL文件分布与简介 1.STL标准头文件(无扩展名):
2.C++ Standard定案前,HP规范的STL头文件(扩展名.h):
3.SGI STL内部私用文件(SGI STL真正实现于此):
SGI STL的编译器组态设置 (“组态(Configure)”的含义是“配置”、“设定”、“设置”等意思,是指用户通过类似“搭积木”的简单方式来完成自己所需要的软件功能,而不需要编写计算机程序,也就是所谓的“组态”。)
SGI STL准备了一个环境组态文件<stl_config.h>定义了许多常量,标志着某些组态成立与否。所有STL文件都会直接或者间接包含这个文件,以条件式写法,让预处理器根据常量取舍一段程序代码。
通过这些组态能够判断对于C++特性的支持情况。
可能会令你困惑的语法 指的是一些C++中的语法层次的特性。
stl.config中出现的组态 1.模板类 中的静态数据成员 初始化 常量__STL_STATIC_TEMPLATE_MEMBER_BUG
(1)首先搞清楚隐式实例化、显式实例化、显式具体化的意思,这里以函数模板来说明含义: 隐式实例化:根据传递的参数类型由编译器根据模板定义生成函数定义; 显式实例化:主动声明对哪种参数类型生成函数定义,使用的是通用模板定义; 显式具体化:对于特定的类型生成与通用模板不同的函数定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> template <typename T>void swap (T &a, T &b) ;template void swap <int >(int &a, int &b);template <> void swap <double >(double &a, double &b);int main (void ) { char a = 'a' , b = 'b' ; swap (a, b); int a1 = 1 , b1 = 2 ; swap (a1, b1); double a2 = 12.8 , b2 = 13.1 ; swap (a2, b2); return 0 ; } template <typename T>void swap (T &a, T &b) { using std::cout; T temp = a; a = b; b = temp; cout << "a = " << a << " b = " << b << '\n' ; } template <> void swap <double >(double &a, double &b) { using std::cout; double temp = a; a = b; b = temp; cout << "b = " << b << " a = " << a << '\n' ; }
(2)其次搞清楚类模板的具体化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 template class ClassType <AnyType>;template <> class ClassType <Antype> { ... }; template <typename T1, typename T2>class Pair {...};template <Typename T1>class Pair <T1, int > {...};
(3)普通类的中的静态成员初始化:在类中声明,在类外初始化。一般初始化放在方法文件中,因为头文件可能被包含很多次,导致多个初始化语句副本。
1 2 3 4 5 6 7 8 class StringBad {public : static int nums; }; int StringBad::nums = 0 ;
(4)模板类中的静态成员数据初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 template <typename T> class TestTemStatic { public : static int knownTypeVar; static T unKnownTypeVar; }; template <typename T> int TestTemStatic<T>::knownTypeVar=50 ;template <> int TestTemStatic<int >::knownTypeVar=2 ;template <> float TestTemStatic<float >::unKnownTypeVar=4.0f ;
MinGW支持这个特性。
2.模板类的部分具体化 常量:__STL_CLASS_PARITIAL_SPECIALIZATION
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std;template <typename I, typename O>struct Test { Test () { cout << "I, 0" << endl; } }; template <typename T>struct Test <T*, T*> { Test () { cout << "T*, T*" << endl; } }; template <typename T>struct Test <const T*, T*> { Test () { cout << "const T*, T*" << endl; } }; int main (void ) { Test<int , char > obj1; Test<int *, int *>obj2; Test<const int *, int *>obj3; return 0 ; }
MinGW支持这个特性。
函数模板的重载 常量:__STL_FUNCTION_TMPL_PARTIAL_ORDER
虽然未文件中声明这个常量的意义与partial specialization of function templates相同,但是实际上并不相同。前者的意义如下表示,后者参考C++语法书籍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class alloc {};template <class T , class Alloc = alloc>class vector {public : void swap (vector<T, Alloc> &) { cout << "swap()" << endl; } }; #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <class T , class Alloc >inline void swap (vector<T, Alloc> &x, vector<T, Alloc> &y) { x.swap (y); } #endif int main (void ) { vector<int > x, y; swap (x, y); return 0 ; }
(我使用的是MinGW8.1.0的64位编译器,这里没有定义这个常量。因此实际上调用的是另一个模板函数。)
显式函数模板参数 常量:__STL_EXPLICIT_FUNCTION_TMPL_ARGS
整个SGI STL没有用到这一个定义
模板成员 常量:__STLMEMBER_TEMPLATES
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> using namespace std;class alloc {};template <typename T, typename Alloc = alloc>class vector {public : typedef T value_type; typedef value_type* iterator; template <typename I> void insert (iterator position, I first, I last) { cout << "insert()" << endl; } }; int main (void ) { int ia[5 ] = {1 , 2 , 3 , 4 , 5 }; vector<int > x; vector<int >::iterator it; x.insert (it, ia, ia + 5 ); return 0 ; }
MinGW支持这个特性。
模板参数可否根据前一个模板参数设定默认值 常量:__STL_LIMITED_DEFAULT_TEMPLATES
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> using namespace std;class alloc {};template <typename T, typename Alloc = alloc, size_t BufSiz = 0 >class deque {public : deque () { cout << "deque" << endl; } }; template <typename T, typename Sequence = deque<T>>class stack {public : stack () { cout << "stack" << endl; } private : Sequence c; }; int main (void ) { stack<int > x; return 0 ; }
MinGW支持这个特性
类模板是否可以拥有non-type参数 常量:__STL_NON_TYPE_TMPL_PARAM_BUG
MinGW支持类模板的非类型参数。
后面还介绍了一些常量,均表示是否支持的一些C++语法功能,但是我采用的版本并不存在这些常量(应该大部分都支持),所以暂时先不看了。
临时对象的产生与运用 临时对象又称为匿名对象(比如按照传递时就会产生一个临时对象)。
刻意创造一个临时对象的方法是:在类型后加一对小括号,可以指定初始值。例如 Shape(3, 5)。相当于调用类型的构造函数到那时不指定名称。
STL将这个技巧应用于仿函数和算法的搭配上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <algorithm> #include <vector> using namespace std;template <typename T>class Print {public : void operator () (const T &elem) { cout << elem << ' ' ; } }; int main (void ) { using std::for_each; using std::vector; int arr[5 ] = {1 , 2 , 3 , 4 , 5 }; vector<int > iv (arr, arr + 5 ) ; for_each(iv.begin (), iv.end (), Print <int >()); return 0 ; }
静态常量整数成员在类内直接初始化 如果类中包含const static integtal 的成员数据,那么可以直接给与初始值。注意这里必须是const;以及integral指的是所有的整数类别,包括int、short、char等。
自增、自减、解引用操作符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Test {public : Test& operator ++() { ++(this ->i_); return *this ; } Test operator ++(int ) { Test temp = *this ; ++(*this ); return temp; } int & operator *() const { return (int &)i_; } private : int i_; };
前闭后开区间表示法 任何STL算法都需要获得一对迭代器所标示的区间,用以表示操作范围。这区间是前闭后开的,即后面的迭代器指向的是最后一个元素的下一个位置。
函数调用操作符operator()
这一整组操作可以是以函数的形式实现,通过函数指针 将函数作为参数传递。