什么是泛型程序设计
我们可以简单的理解为:使用模板的程序设计就是泛型程序设计。就像我们我们可以简单的理解面向对象程序设计就是使用虚函数的程序设计一样。
STL是什么
作为一个C++程序设计者,STL是一种不可忽视的技术。Sandard Template Library (STL):
标准模板库,更准确的说是 C++ 程序设计语言标准模板库。学习过MFC的人知道,MFC是微软公司创建的 C++ 类库。而与之类似的是 STL 是模板库,只不过 STL 是 ANSI/ISO 标准的一部分,而 MFC 只不过是微软的一个产品而已。也就是说STL是所有C++编译器和所有操作系统平台都支持的一种库,说它是一种库是因为,虽然STL是一种标准,也就是说对所有的编译器来说,提供给C++程序设计者的接口都是一样的。也就是说同一段STL代码在不同编译器和操作系统平台上运行的结果都是相同的,但是底层实现可以是不同的。 令人兴奋的是,STL的使用者并不需要了解它的底层实现。 试想一下,如果我们有一把能打开所有锁的钥匙,那将是多么令人疯狂啊。嘎嘎。这个歪梦我做了20多年鸟。
STL的目的是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成的组件。STL现在是C++的一部分,因此不用额外安装什么。它被内建在你的编译器之内。
为什么我们需要学习STL
下面让我们来看几段代码吧:(你觉得头疼就不要看了)
复制代码
好懂吧,除了那个std有点让人不舒服以外?这是一段普通的没有使用STL的C++代码。再看下面一段:
复制代码
如果你真的没有接触过STL的话,你会问,呀,vector 是啥呀?我会告诉你,那是一排美女。嘎嘎。这可不是个比喻,表想歪鸟。这是一段纯种的STL代码,看到尖括号了吧,知道那是模板了吧。看到a.push_back(5),a.begin(),a.end()你不感觉奇怪么?可是我们并没有定义这些函数啊。
复制代码
这一段你又看到了新东西了吧,iterator???不罗嗦了,等你看完这篇文章,回头再看就简单了。在正式介绍STL之前,我们需要花点时间来了解一下模板和命名空间。
关于模板的其他细节,读者可以参阅《C++ Templates 中文版》(有点费脑子哦)。在这里,我只简单的介绍一下模板类和函数模板的概念。
模板是C++中实现代码重用机制的一种工具,可以实现类型参数化,把类型定义为参数。函数模板和类模板允许用户构造模板函数和模板类。
图1
下面我们来看一段函数模板的例子:
复制代码
下面再看看,类模板:
复制代码
命名空间(名字空间)
命名空间是C++的一种机制,用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。命名空间用关键字namespace 来定义:
复制代码
一个namespace是指一个具名的范围(named scope)。namespace被用来将相关的声明划归在一起,将不相关的代码部分隔开。命名空间只是命名了一个特殊的作用域,当程序很大,而且需要多人合作的时候,命名空间就显得特别的重要。比如2个程序员A,B 在同一个程序中定义了函数 pop(),如果没有使用命名空间,则会出错,而且这种错误难以检测出来。为了安全起见,他们可以定义不同的命名空间 A和B,在用的时候可以使用A::pop()和B::pop()来区分。
在STL中,标准库的全部成员在预先定义的命名空间std中。如果要用类模板vector ,有两种方法:一是在程序的前面添加预处理指令:
复制代码
第二种方法是:
复制代码
我们可以简单的理解为:使用模板的程序设计就是泛型程序设计。就像我们我们可以简单的理解面向对象程序设计就是使用虚函数的程序设计一样。
STL是什么
作为一个C++程序设计者,STL是一种不可忽视的技术。Sandard Template Library (STL):
标准模板库,更准确的说是 C++ 程序设计语言标准模板库。学习过MFC的人知道,MFC是微软公司创建的 C++ 类库。而与之类似的是 STL 是模板库,只不过 STL 是 ANSI/ISO 标准的一部分,而 MFC 只不过是微软的一个产品而已。也就是说STL是所有C++编译器和所有操作系统平台都支持的一种库,说它是一种库是因为,虽然STL是一种标准,也就是说对所有的编译器来说,提供给C++程序设计者的接口都是一样的。也就是说同一段STL代码在不同编译器和操作系统平台上运行的结果都是相同的,但是底层实现可以是不同的。 令人兴奋的是,STL的使用者并不需要了解它的底层实现。 试想一下,如果我们有一把能打开所有锁的钥匙,那将是多么令人疯狂啊。嘎嘎。这个歪梦我做了20多年鸟。
STL的目的是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成的组件。STL现在是C++的一部分,因此不用额外安装什么。它被内建在你的编译器之内。
为什么我们需要学习STL
- STL是 C++的ANSI/ISO 标准的一部分,可以用于所有C++语言编译器和所有平台(Windows/Unix/Linux..)。STL的同一版本在任意硬件配置下都是可用的;
- STL 提供了大量的可复用软件组织。例如,程序员再也不用自己设计排序,搜索算法了,这些都已经是STL的一部分了。嘎嘎,有意思吧;
- 使用STL 的应用程序保证了得到的实现在处理速度和内存利用方面都是高效的,因为STL设计者们已经为我们考虑好了;
- 使用STL编写的代码更容易修改和阅读,这是当然的鸟。因为代码更短了,很多基础工作代码已经被组件化了;
- 使用简单,虽然内部实现很复杂;
下面让我们来看几段代码吧:(你觉得头疼就不要看了)
- //stl_cpp_1.cpp
- #include <iostream>
- double mean(double *array, size_t n)
- {
- double m=0;
- for(size_t i=0; i<n; ++i){
- m += array[i];
- }
- return m/n;
- }
- int main(void)
- {
- double a[] = {1, 2, 3, 4, 5};
- std::cout<<mean(a, 5)<<std::endl; // will print 3
- return 0;
- }
- //stl_cpp_2.cpp
- #include <vector>
- #include <iostream>
- int main(void)
- {
- std::vector<double> a;
- std::vector<double>::const_iterator i;
- a.push_back(1);
- a.push_back(2);
- a.push_back(3);
- a.push_back(4);
- a.push_back(5);
- for(i=a.begin(); i!=a.end(); ++i){
- std::cout<<(*i)<<std::endl;
- }
- return 0;
- }
- //stl_cpp_3.cpp
- #include <vector>
- #include <iostream>
- int main(void)
- {
- std::vector<int> q;
- q.push_back(10);
- q.push_back(11);
- q.push_back(12);
- std::vector<int> v;
- for(int i=0; i<5; ++i){
- v.push_back(i);
- }
- std::vector<int>::iterator it = v.begin() + 1;
- it = v.insert(it, 33);
- v.insert(it, q.begin(), q.end());
- it = v.begin() + 3;
- v.insert(it, 3, -1);
- it = v.begin() + 4;
- v.erase(it);
- it = v.begin() + 1;
- v.erase(it, it + 4);
- v.clear();
- return 0;
- }
关于模板的其他细节,读者可以参阅《C++ Templates 中文版》(有点费脑子哦)。在这里,我只简单的介绍一下模板类和函数模板的概念。
模板是C++中实现代码重用机制的一种工具,可以实现类型参数化,把类型定义为参数。函数模板和类模板允许用户构造模板函数和模板类。
图1
下面我们来看一段函数模板的例子:
- //stl_cpp_4.cpp
- #include<iostream.h>
- #include<string.h>
- //定义函数模板
- template<class T> //template 是关键字,T 表示一种待实例化的类型
- //template<typename T> 也是对的
- T max(T a, T b)//函数模板,函数名为 max,此函数有2个T类型的参数,返回类型为T
- {
- return (a>b)?a:b;
- }
- //在此例实例化的时候,T可以是多种类型的,int,char,string…
- int main(void)
- {
- int x=2,y=6;
- double x1=9.123,y1=12.6543;
- cout<<"把T实例化为int:"<<max(x,y)<<endl;//实例化函数模板,把T实例化为int
- cout<<"把T实例化为double:"<<max(x1,y1)<<endl;
- //实例化函数模板,把T实例化为double
- getchar(); //这一行代码用来在dos下查看结果,也可以用cin.get();
- }
- //stl_cpp_5.cpp
- #include<iostream.h>
- //定义名为ex_class的类模板
- template < typename T> class ex_class
- {
- T value;
- public:
- ex_class(T v) { value=v; }
- void set_value(T v) { value=v; }
- T get_value(void) {return value;}
- };
- //main()函数中测试ex_class类模板
- int main(void)
- {
- //测试int类型数据
- ex_class <int> a(5),b(10);
- cout<<"a.value:"<<a.get_value()<<endl;
- cout<<"b.value:"<<b.get_value()<<endl;
- //测试char类型数据
- ex_class <char> ch(''A'');
- cout<<"ch.value:"<<ch.get_value()<<endl;
- ch.set_value(''a'');
- cout<<"ch.value:"<<ch.get_value()<<endl;
- //测试double类型数据
- ex_class <double> x(5.5);
- cout<<"x.value:"<<x.get_value()<<endl;
- x.set_value(7.5);
- cout<<"x.value:"<<x.get_value()<<endl;
- }
命名空间是C++的一种机制,用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。命名空间用关键字namespace 来定义:
- //stl_cpp_6.cpp
- #include <iostream>
- using namespace std;
- namespace printA
- {
- print() {cout<<"using namespace printA….."<<endl; };
- }
- namespace printB
- {
- print() {cout<<"using namespace printB….."<<endl; };
- }
- int main(void)
- {
- printA::print(); //测试命名空间printA, ::是作用域解析运算符
- printB::print();
- }
- 命名空间可以嵌套定义:
- namespace A
- {
- functiong1(){};
- namespace B
- { }
- }
在STL中,标准库的全部成员在预先定义的命名空间std中。如果要用类模板vector ,有两种方法:一是在程序的前面添加预处理指令:
- #include <vector>
- using namespace std;
- #include <vector>
- using std::vector;
动态绑定和静态绑定
所谓绑定是指,对于参与多态行为的类型,他们具有多态行为的接口是在公共基类的设计中就预先确定的。而非绑定则对于参与多态行为的类型,他们的接口没有预先定义。
在C++中通过继承实现的多态是动态绑定,通过模板实现的多态是静态绑定。动态绑定的接口是在运行期间(动态)完成的,静态绑定的接口是在编译期间(静态)完成的。好了,有了以上的知识我们可以来学习STL 了。
STL 的组成 STL有三大核心部分:容器(Container)、算法(Algorithms)、迭代器(Iterator),容器适配器(container adaptor),函数对象(functor),除此之外还有STL其他标准组件。
知道了吧?嘎嘎,当然了,你猜到了,那是我在瞎扯蛋。 下面让我们来看看专家是怎么说的:
容器 STL中的容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。 在本文中,我将介绍list,vector,deque等队列容器,和set和multisets,map和multimaps等关联容器,一共7种基本容器类。 队列容器(顺序容器):队列容器按照线性排列来存储T类型值的集合,队列的每个成员都有自己的特有的位置。顺序容器有向量类型、双端队列类型、列表类型三种。 基本容器——顺序容器 向量(vector容器类):#include <vector>,vector是一种动态数组,是基本数组的类模板。其内部定义了很多基本操作。既然这是一个类,那么它就会有自己的构造函数。vector 类中定义了4中种构造函数: 默认构造函数,构造一个初始长度为0的空向量, 如:vector<int> v1; 带有单个整形参数的构造函数,此参数描述了向量的初始大小。这个构造函数还有一个可选的参数,这是一个类型为T的实例,描述了各个向量种各成员的初始值; 如:vector<int> v2(init_size,0); 如果预先定义了:int init_size;他的成员值都被初始化为0; 复制构造函数,构造一个新的向量,作为已存在的向量的完全复制, 如:vector<int> v3(v2); 带两个常量参数的构造函数,产生初始值为一个区间的向量。区间由一个半开区间[first,last](MS word的显示可能会有问题,first前是一个左方括号,last后面是一个右圆括号)来指定。 如:vector<int> v4(first,last) 下面一个例子用的是第四种构造方法,其它的方法读者可以自己试试。
在Java里面也有向量的概念。Java中的向量是对象的集合。其中,各元素可以不必同类型,元素可以增加和删除,不能直接加入原始数据类型。 双端队列(qeque容器类):#include <deque> deque(读音:deck,意即:double queue)容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据: push_front()。此外deque也不支持与vector的capacity()、reserve()类似的操作。
上面我们演示了deque如何进行插入删除等操作,像erase(),assign()是大多数容器都有的操作。关于deque的其他操作请参阅附录。
|