C++ 泛型(模板与容器)

一、泛型的基本思想:

泛型编程(Generic Programming)是一种语言机制,通过它可以实现一个标准的容器库。
像类一样,泛型也是一种抽象数据类型,但是泛型不属于面向对象,它是面向对象的补充和发展。在面向对象编程中,当算法与数据类型有关时,面向对象在对算法的抽象描述方面存在一些缺陷。

泛型在C++中的主要实现为模板函数和模板类。
通常使用普通的函数实现一个与数据类型有关的算法是很繁琐的,比如两个数的加法,要
考虑很多类型:

int add(int a,int b) { return a+b; }
float add(float a,float b) { return a+b; }

在C++中可以通过函数重载来解决这个问题,但是反复写相同算法的函数是比较辛苦的,更重要的是函数重载是静态编译,运行时占用过多内存。
在此我们可以用C++的模板函数来表达通用型的函数,如下:

template<typename T>            // 模板声明
T add(T a,T b) { return a+b; }  // 注意形参和返回值的类型

这时C++编译器会根据add函数的参数类型来生成一个与之对应的带具体参数类型的函数并调用。

#include <iostream>
using namespace std;

template <typename T>
T add(T a,T b)   //注意形参和返回类型
{ 
    return a+b;
} 
void main()
{
    int num1, num2, sum; 
    cin>>num1>>num2;
    sum=add(num1,num2); //用int匹配模版参数T,若sum,num1,num2类型不一致则无法匹配。
    cout<<sum;
}
函数模板的性质
  1. 函数模板并不是真正的函数,它只是C++编译生成具体函数的一个模子。
  2. 函数模板本身并不生成函数,实际生成的函数是替换函数模板的那个函数,比如上例中的add(sum1,sum2),这种替换是编译期就绑定的。
  3. 函数模板不是只编译一份满足多重需要,而是为每一种替换它的函数编译一份。
  4. 函数模板不允许自动类型转换。
  5. 函数模板不可以设置默认模板实参。比如template<typenameT=0>不可以。
C++模版函数 / 类的语法

下面的几种写法是等效的并且class和typename是可以互换的。

C++模版函数语法
template <typename模版参数列表…>
函数返回类型 函数名(形参列表…)

template <typenameT1, typename T2>         // template <classT1,class T2> 
T1 fun(T1, T2, int )
{ //…..}
template <typenameT1,T2> T1 fun(T1, T2, int )
{ //…..}

=============================================
C++模版类的语法
template <class模版参数列表…>
class 类名
{ //类体}
成员的实现…

//类声明部分,有两个模板参数T1,T2
template <classT1, class T2 >
class A {
    private:
    int a;
    T1 b; //成员变量也可以用模板参数
public:
    int fun1(T1 x, int y );
    T2 fun2(T1 x, T2 y);
}

//类实现部分
template <classT1, class T2 >
int A<T1>:: fun1(T1 x, int y ){//实现…… }
template <classT1, class T2 >
T2 A<T1,T2>:: fun2(T1 x, T2 y) {//实现…… }

//使用类A
int main( ) {
    A<int, float> a;        //定义对象a,并用int替换T1, float替换T2
                            //实例化a,调用a的属性和方法……
}

由上例可以看出, 类模板参数T1,T2对类的成员变量和成员函数均有效。
在C++编程中,当你要实现的一个类的某些成员函数和成员变量的算法
数据类型有关,可以考虑用类模板。C++版的数据结构算法大都用类模板实现。

类模板的性质

1)类模板不是真正的类,它只是C++编译器生成具体类的一个模子。
2)类模板可以设置默认模板实参。

二、C++ STL简介

STL(Standard Template Library,标准模板库)是C++对泛型编程思想的实现,最早是惠普实验室开发的。
在被引入C++之前该技术就已经存在了很长的一段时间。后来STL成为ANSI/ISO C++标准的一部分。各个C++厂商也有各自相应的模板库,这些库效率可能很高,但可移植性不一定好。

STL以迭代器(Iterators)和容器(Containers)为基础,是一种泛型算法(Generic Algorithms)库,容器的存在使这些算法有东西可以操作。迭代器(Iterators)是STL的核心,它们是泛型指针,是一种指向其他对象(objects)的对象,迭代器能够遍历由对象所形成的区间(range)。

STL广义上分为三类:algorithm(算法)container(容器)iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
在C++标准中,STL被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、 <list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>

2.1 算法(algorithm)

STL提供了大约100个实现算法的模版函数,算法部分主要由头文件, 和组成。
<algorithm>是所有STL头文件中最大的一个,它是由一大堆模板函数组成的,其中常用到的功能范围涉及到比较、
交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。
<numeric>体积很小,只包括一些简单数学运算的模板函数。
<functional>中则定义了一些模板类,用以声明函数对象。

2.2 容器(container)(又称集合collection)

在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要
求很高的部分时,数据结构的选择就显得更加重要。
通过设置一些模版类,STL容器对最常用的数据结构提供了支持,这些模板的参数允许指定容器中元素的数据类型,可以将许多重复而乏味的工作简化。
如下表:

数据结构 实现头文件 说明
向量(vector) 顺序容器 <vector> 动态数组,操作时间:尾部O(1),头部O(N),内存连续
列表(双链表)(list) 顺序容器 <list> insert和splice方法,前者复制副本,
后者将原始区间移动
双端队列(deque) 顺序容器 <deque> 双端队列,支持随机访问,头尾操作都是O(1)
栈(stack) 容器适配器 <stack> 底层是vector
队列(queue) 容器适配器 <queue> 先进先出,底层使用deque
优先队列(priority_queue) 容器适配器 <queue> 底层使用vector,默认是最大的元素放在队首
集合(set) 有序关联容器 <set> 它里面不会有重复的数据,并且会自动排序!
查找的时间复杂度大概在O(log(n))
多重集合(multiset) 有序关联容器 <set> 内部以平衡二叉树实现
映射(map) 有序关联容器 <map> 内部实现了红黑树,元素有序性提高了运行效率;
空间占用率高
多重映射(multimap) 有序关联容器 <map>
无序集合(unordered_set) 无序关联容器 <unordered_set>
无序多重集合(unordered_multiset) 无序关联容器 <unordered_multiset>
无序映射(unordered_map) 无序关联容器 <unordered_map> 内部实现了哈希表,查找速度极快;
哈希表建立比较耗费时间
无序多重映射(unordered_multimap) 无序关联容器 <unordered_multimap>

4种无序关联容器:unordered_setunordered_multisetunordered_mapunordered_multimap为c++11提供。
关于有序、无序映射容器:https://blog.csdn.net/vir_lee/article/details/80515957

2.3 迭代器(iterator)

迭代器是一种允许程序员检查容器内元素,并实现元素遍历的数据类型。C++标准库为每一种标准容器定义了一种迭代器类型。迭代器类型提供了比下标操作更一般化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器(比如数组)支持下标操作。因为迭代器对所有的容器都适用,现代C++程序更倾向于使用迭代器而不是下标操作访问容器元素。
  迭代器从作用上来说是STL最基本的部分,迭代器在STL中用来将算法和容器联系起来,起着一种黏和剂的作用。几乎STL提供的所有算法都是通过迭代器存取元素序列进行工作的,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。
  迭代器部分主要由头文件, 和组成。是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,中提供了迭代器使用的许多方法, 为容器中的元素分配存储空间,同时也为某些算法执行期间产生的临时对象提供机制,中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。

迭代器一般分为五种:
Input Iterator 只读,单向移动,如STL中的 istream_iterator
Output Iterator 只写,单向移动,如STL中的 ostream_iterator
Forward Iterator 具有读、写性,单向移动。
Bidirections Iterator ​​​​​​​具有读、写性,双向移动。
​​​​​​​Random Access Iterator 具有读、写性,随机访问

算法:http://www.cplusplus.com/reference/algorithm/
容器:https://en.cppreference.com/w/cpp/container
https://blog.csdn.net/xinzheng_wang/article/details/83984151
https://blog.csdn.net/jiachang98/article/details/79170419
容器全面总结:https://blog.csdn.net/u014465639/article/details/70241850
vector的嵌套:https://zhuanlan.zhihu.com/p/43596854
multiset用法详解:http://c.biancheng.net/view/545.html

发布了386 篇原创文章 · 获赞 592 · 访问量 72万+

猜你喜欢

转载自blog.csdn.net/wsp_1138886114/article/details/103990019
今日推荐