c++——模板、泛型

容器

  • 容器(container)用于存放数据的类模板
  • 容器都是类模板。它们实例化后就成为容器类。用容器类定义的对象称为容器对象
  • 例如,vector<int>是一个容器类的名字,vector<int> a;就定义了一个容器对象 a
  • 容器分为顺序容器和关联容器
  • 顺序容器有以下三种:可变长动态数组 vector、双端队列 deque、双向链表 list
  • 它们之所以被称为顺序容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的
  • 关联容器有以下四种:set、multiset、map、multimap
  • 关联容器内的元素是排序的,因此插入元素时不能指定位置
  • 默认情况下,关联容器中的元素是从小到大排序(或按关键字从小到大排序)的
  • 数组是一种类似于vector的数据结构,但数组大小确定,不能随意向其中增加元素

迭代器

  • 要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行
  • 迭代器是一个变量,相当于容器和操纵容器的算法之间的中介
  • 迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似
  • 通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素

模板

  • 函数模板:重载的进一步抽象,只需定义一个函数体即可用于所有类型
  • 在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的实参自动推断数据类型。这就是类型的参数化
  • 值(Value)和类型(Type)是数据的两个主要特征,它们在C++中都可以被参数化
  • 所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)
  • typename关键字也可以使用class关键字替代
  • 类模板中定义的类型参数可以用在类声明和类实现中,类模板的目的同样是将数据的类型参数化
  • 一但声明了类模板,就可以将类型参数用于类的成员函数和成员变量了
  • 与函数模板不同的是,类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型
  • C++ 模板也是被迫推出的,最直接的动力来源于对数据结构的封装
  • 数据结构中每份数据的类型无法提前预测,而 C++ 又是强类型的,数据的种类受到了严格的限制
  • STL(Standard Template Library,标准模板库)就是 C++ 对数据结构进行封装后的称呼
  • 并非所有的类型都使用同一种算法,有些特定的类型需要单独处理,为了满足这种需求,C++ 允许对函数模板进行重载
  • 模板(Templet)并不是真正的函数或类,它仅仅是编译器用来生成函数或类的一张“图纸”。模板不会占用内存,最终生成的函数或者类才会占用内存。由模板生成函数或类的过程叫做模板的实例化(Instantiate),相应地,针对某个类型生成的特定版本的函数或类叫做模板的一个实例(Instantiation)
  • 模板也可以看做是编译器的一组指令,它命令编译器生成我们想要的代码
  • 通过类模板创建对象时,一般只需要实例化成员变量和构造函数
  • 通过类模板创建对象时并不会实例化所有的成员函数,只有等到真正调用它们时才会被实例化;如果一个成员函数永远不会被调用,那它就永远不会被实例化
  • 编译是针对单个源文件的,只要有函数声明,编译器就能知道函数调用是否正确;而将函数调用和函数定义对应起来的过程,可以延迟到链接时期。正是有了链接器的存在,函数声明和函数定义的分离才得以实现
  • 程序员惯用的做法是将模板的声明和定义都放到头文件中
  • 模板的实例化是按需进行的,用到哪个类型就生成针对哪个类型的函数或类,不会提前生成过多的代码
  • 模板的实例化是由编译器完成的,而不是由链接器完成的
  • 在实例化过程中需要知道模板的所有细节,包含声明和定义

泛型

  • 模板所支持的类型是宽泛的,没有限制的,我们可以使用任意类型来替换,这种编程方式称为泛型编程(Generic Programming)
  • 推出泛型最直接的动力来源于对通用算法(查找、排序、删除)的封装

猜你喜欢

转载自www.cnblogs.com/cxc1357/p/11966937.html