知识内容:
1.STL介绍
2.C++基础知识复习
3.C++中的模板简单介绍
4.STL组成部分
一、STL介绍
1.什么是STL?
学过C++的应该都听说过STL,那么什么是STL呢?STL是Standard Template Library的简称,翻译为标准模板库,是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件。STL现在是C++的一部分,因此不用安装额外的库文件,只用在使用STL时包含相应头文件就行了。
2.STL组成部分
在C++标准中,STL被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<array>、<vector>、<list>、<forward_list>、<map>、<unordered_map>、<memory>、<numeric>、<queue>、<set>、<unordered_set>、<stack>和<utility>
STL可分为容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters)、算法(algorithms)、仿函数(functors)六个部分,我将在下面详细解释。
二、C++基础知识复习
要学习STL,毫无疑问C++基础必须掌握,在这就稍微复习一下C++的基础知识
1.C++基本输入输出:
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int n; 7 cin << n; //C++基本输入 8 cout << "You enter " << n << endl; //C++基本输出 9 }
2.C++引用:
C++定义引用的表示方法与定义指针相似,只是用&代替了*。引用是c++对C语言的重要扩充。引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名;
1 //交换两个数的值 2 #include <iostream> 3 using namespace std; 4 5 void swap_number(int &i, int &j) 6 { 7 int temp = i; 8 i = j; 9 j = temp; 10 } 11 12 int main() 13 { 14 int a, b; 15 cin >> a >> b; 16 swap_number(a, b); 17 cout << a << " " << b << endl; 18 return 0; 19 }
3.C++类的基础知识:
类: 具有相同行为和属性的对象的集合
构造函数: 类的对象创建时就会被自动调用
析构函数: 类的对象销毁时就会被自动调用
成员变量、成员函数 -> 通过对象来访问
static成员变量: 一个类所有对象共享的数据,不属于某个对象
static成员函数: 一个类所有对象共享的函数,不能访问非static成员变量,不属于某个对象,应用于单例模式中
类的访问控制: private、protected、public
继承: 一个类共享另一个类或多个类的数据或方法,私有成员不会被继承
3种继承: public继承、protected继承、private继承,用的最多的是public继承
4.绑定:
静态绑定: 编译阶段确定函数调用 例如: 函数重载
动态绑定: 运行时确定函数调用 例如: 虚函数
5.多态:
多态一词最初来源于希腊语,意思是具有多种形式或形态的情形,在C++中是指同样的消息被不同类型的对象接收时导致不同的行为,这里讲的消息就是指对象的成员函数的调用,而不同的行为是指不同的实现。也就是调用了不同的函数。
6.操作符重载:
C++中可以实现操作符重载,通俗地讲就是在某些时候改变运算符的功能
使用运算符重载实现矩阵加减乘:
1 //矩阵的加减乘 2 #include <iostream> 3 using namespace std; 4 #include <cstdio> 5 int const N = 2; 6 int const M = 2; 7 8 class Matrix{ 9 private: 10 double ptr[N][M]; 11 public: 12 Matrix(double i=0, double j=0, double k=0, double l=0); 13 void Show(); 14 Matrix operator+(Matrix a); 15 Matrix operator-(Matrix a); 16 Matrix operator*(Matrix a); 17 }; 18 19 Matrix::Matrix(double i, double j, double k, double l) 20 { 21 ptr[0][0] = i; 22 ptr[0][1] = j; 23 ptr[1][0] = k; 24 ptr[1][1] = l; 25 } 26 27 Matrix Matrix::operator+(Matrix a) 28 { 29 Matrix temp; 30 for(int i=0;i<N;i++) 31 for(int j=0;j<M;j++) 32 { 33 temp.ptr[i][j] = this->ptr[i][j] + a.ptr[i][j]; 34 } 35 return temp; 36 } 37 38 Matrix Matrix::operator-(Matrix a) 39 { 40 Matrix temp; 41 for(int i=0;i<N;i++) 42 for(int j=0;j<M;j++) 43 { 44 temp.ptr[i][j] = this->ptr[i][j] - a.ptr[i][j]; 45 } 46 return temp; 47 } 48 49 Matrix Matrix::operator*(Matrix a) 50 { 51 Matrix temp; 52 for(int i=0;i<N;i++) 53 for(int j=0;j<M;j++) 54 for(int k=0;k<M;k++) 55 { 56 temp.ptr[i][j] += this->ptr[i][k]*a.ptr[k][j]; 57 } 58 return temp; 59 } 60 61 void Matrix::Show() 62 { 63 for(int i=0;i<N;i++) 64 { 65 cout << "( "; 66 for(int j=0;j<M;j++) 67 { 68 cout << ptr[i][j] << " "; 69 } 70 cout << ")" << endl; 71 } 72 cout << endl; 73 } 74 75 int main() 76 { 77 Matrix first(1,2,3,4), second(2,6,8,10), total, sub, mul, div; 78 cout << "这是两个矩阵: " << endl; 79 first.Show(); 80 second.Show(); 81 cout << "两个矩阵相加得: " << endl; 82 total = first + second; 83 total.Show(); 84 cout << "两个矩阵相减得: " << endl; 85 sub = first - second; 86 sub.Show(); 87 cout << "两个矩阵相乘得: " << endl; 88 mul = first*second; 89 mul.Show(); 90 91 92 return 0; 93 }
7.C++的异常处理:
C++中处理异常的机制由检查、抛出和捕获三个部分组成,分别由三种语句来完成: try(检查)、throw(抛出)、catch(捕获),
把需要检查的语句放在try中,throw用在出现异常时发出一个信息,而catch用来捕获异常,并在捕获异常后对其进行处理。
8.C++的命名空间:
(1)定义与解释:
假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
(2)C++标准库命名空间的使用:
C++标准库内的所有表示符都被定义在一个名为std的命名空间中,使用标识符时可以直接在前面加上std::即可,示例:
1 #include <iostream> 2 3 int main() 4 { 5 std::cout << "C++命名空间第一种使用方法" << std::endl;
return 0; 6 }
但是这样使用命名空间std也有弊端,当程序中使用的标识符较多时,写起来比较麻烦,可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称,示例:
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 cout << "C++命名空间第二种使用方法" << endl; 7 cout << "直接使用using namespace指令" << endl; 8 9 return 0; 10 }
三、C++中的模板简单介绍
1.C++为什么需要模板功能
C++需要模板这个功能的原因很简单,就是计算机实在是太傻了。在大多数人的眼中,计算机即神秘又能干,而在程序员的眼中,计算机实在是又蠢又笨。
只不过运算的比人类速度快,记忆力好,但是如果我们不给它指令,计算机就什么干不了,就算是给指令, 写一个程序,计算机也不是很灵活。比如说在C++
中同样一个加法,要对不同的数据类型给出不同代码,要写不同的函数,于是有人提出了一种新思想-泛型编程,那么什么是泛型编程呢?
2.泛型编程
泛型编程最初提出的动机很简单,就是要发明一种语言机制,能实现:
(1)一个通用的标准容器库。何为通用的标准容器库?比如用一个list类能存放所有的数据类型的对象:整形、浮点型、字符串类型、自定义对象类型、、、
(2)编写完全一般化并可以重复使用的算法,其效率与针对某特定数据结构设计的算法不相上下。泛型是指在多种数据类型上皆可操作的含义,这样的算法与
数据结构相分离,其中的算法是泛型的,不与任何特定数据结构或对象类型联系在一起,从而提供了工作效率。
3.C++的模板
我们为了让程序更加智能化,C++很需要泛型这种新的编程方式,于是引入了模板这个功能,也就是说在C++中引入了关键字template
使用模板是为了实现泛型,可以减轻编程的工作量,增强函数的重用性。
比如说将两个变量相加的函数add,如果不是用模板来实现,我们需要针对不同的类型写多个功能相同的函数,例如int、float等,而使用了模板后只需使用以下一个函数即可:
1 template <class T> 2 T add(T m, T n) 3 { 4 return m+n; 5 }
调用时可以说明类型,例如: add<int>(3,5); 但是也可以不用使用类型,函数会根据参数的类型自动确定类型: add(3,5);
当然,还可以自定义数据类型,甚至自己定义的类
四、STL组成部分
STL可分为容器(containers)、算法(algorithms)、迭代器(iterators)、仿函数(functors)、配接器(adapters)、空间配置器(allocator)六个部分
1、容器
作为STL的最主要组成部分--容器,分为向量(vector),双端队列(deque),表(list),队列(queue),堆栈(stack),集合(set),多重集合(multiset),映射(map),多重映射(multimap)。
容器 |
特性 |
所在头文件 |
向量vector |
可以用常数时间访问和修改任意元素,在序列尾部进行插入和删除时,具有常数时间复杂度,对任意项的插入和删除就有的时间复杂度与到末尾的距离成正比,尤其对向量头的添加和删除的代价是惊人的高的 |
<vector> |
双端队列deque |
基本上与向量相同,唯一的不同是,其在序列头部插入和删除操作也具有常量时间复杂度 |
<deque> |
表list |
对任意元素的访问与对两端的距离成正比,但对某个位置上插入和删除一个项的花费为常数时间。 |
<list> |
队列queue |
插入只可以在尾部进行,删除、检索和修改只允许从头部进行。按照先进先出的原则。 |
<queue> |
堆栈stack |
堆栈是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项。即按照后进先出的原则 |
<stack> |
集合set |
由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序,具有快速查找的功能。但是它是以牺牲插入删除操作的效率为代价的 |
<set> |
多重集合multiset |
和集合基本相同,但可以支持重复元素具有快速查找能力 |
<set> |
映射map |
由{键,值}对组成的集合,以某种作用于键对上的谓词排列。具有快速查找能力 |
<map> |
多重集合multimap |
比起映射,一个键可以对应多了值。具有快速查找能力 |
<map> |
2、算法
算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。<algorithm>是所有STL头文件中最大的一个,它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范 围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。<functional>中则定义了一些模板类,用以声明函数对象。
STL的算法也是非常优秀的,它们大部分都是类属的,基本上都用到了C++的模板来实现,这样很多相似的函数就不用自己写了,只要用函数模板就可以了。
我们使用算法的时候,要针对不同的容器,比如:对集合的查找,最好不要用通用函数find(),它对集合使用的时候,性能非常的差,最好用集合自带的find()函数,它针对了集合进行了优化,性能非常的高。
3、迭代器
它的具体实现在<itertator>中,我们完全可以不管迭代器类是怎么实现的,大多数的时候,把它理解为指针是没有问题的(指针是迭代器的一个特例,它也属于迭代器),但是,决不能完全这么做。
迭代器功能 |
||
输入迭代器 Input iterator |
、Reads forward |
istream |
输出迭代器 Output iterator |
向前写 Writes forward |
ostream,inserter |
前向迭代器 Forward iterator |
向前读写 Read and Writes forward |
|
双向迭代器 Bidirectional iterator |
向前向后读写 Read and Writes forward and backward |
list,set,multiset,map,mul timap |
随机迭代器 Random access iterator |
随机读写 Read and Write with random access |
vector,deque,array,string |
4、仿函数
(1)仿函数的概念:
仿函数,又或叫做函数对象,是STL六大组件之一;仿函数虽然小,但却极大的拓展了算法的功能,几乎所有的算法都有仿函数版本。例如,查找算法find_if就是对find算法的扩展,标准的查找是两个元素相等就找到了,但是什么是相等在不同情况下却需要不同的定义,如地址相等,地址和邮编都相等,虽然这些相等的定义在变,但算法本身却不需要改变,这都多亏了仿函数。仿函数(functor)又称之为函数对象(function object),其实就是重载了()操作符的struct,没有什么特别的地方
1 class compare_class 2 { 3 public: 4 bool operator()(int A, int B)const{return A < B;} 5 }; 6 7 // Declaration of C++ sorting function. 8 template<class ComparisonFunctor> 9 void sort_ints(int* begin_items, int num_items, ComparisonFunctor c); 10 11 int main() 12 { 13 int items[]={4, 3, 1, 2}; 14 compare_class functor; 15 sort_ints(items, sizeof(items)/sizeof(items[0]), functor); 16 17 return 0; 18 }
(3)为什么要使用仿函数:
仿函数比一般的函数灵活;仿函数有类型识别,可以作为模板参数;执行速度上仿函数比函数和指针要更快的。
5、适配器(配接器)
适配器是用来修改其他组件接口的STL组件,是带有一个参数的类模板(这个参数是操作的值的数据类型)。STL定义了3种形式的适配器:容器适配器,迭代器适配器,函数适配器。
(1)容器适配器:
包括栈(stack)、队列(queue)、优先(priority_queue)。使用容器适配器,stack就可以被实现为基本容器类型(vector,dequeue,list)的适配。可以把stack看作是某种特殊的vctor,deque或者list容器,只是其操作仍然受到stack本身属性的限制。queue和priority_queue与之类似。容器适配器的接口更为简单,只是受限比一般容器要多。
(2)迭代器适配器:
修改为某些基本容器定义的迭代器的接口的一种STL组件。反向迭代器和插入迭代器都属于迭代器适配器,迭代器适配器扩展了迭代器的功能。
(3)函数适配器:
通过转换或者修改其他函数对象使其功能得到扩展。这一类适配器有否定器(相当于"非"操作)、绑定器、函数指针适配器。函数对象适配器的作用就是使函数转化为函数对象,或是将多参数的函数对象转化为少参数的函数对象。
6、空间配置器
空间配置器,也叫内存配置器,空间配置器代表一种特定的内存模型,并提供一种抽象概念,便于将内存的申请转换成对内存的直接调用。配置器主要用于将算法和容器的实现隔离于物理存储细节之外。