C++学习笔记(三)
1、模板
1.1 模板的概念
模板就是建立通用的模具,大大提高复用性
模板的特点:
- 模板不可以直接使用,它只是一个框架
- 模板的通用并不是万能的
1.2 函数模板
- C++另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
1.2.1 函数模板语法
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
- template——声明创建模板
- typename——表明其后面的符号是一种数据类型,可以用class类型
- T——通用的数据类型,名称可以替换,通常为大写字母
两种方式使用函数模板
-
自动类型推导
-
显示指定类型
1.2.2 函数模板注意事项
-
自动类型推导,必须推导出一致的数据类型T,才可以使用
-
模板必须要确定出T的数据类型,才可以使用
1.2.3 普通函数与函数模板的区别
-
普通函数调用时可以发生自动类型转换(隐式类型转换)
-
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
-
如果利用显示指定类型的方式,可以发生隐式类型转换
建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
1.2.4 普通函数与函数模板调用规则
-
如果函数模板和普通函数都可以调用,优先调用普通函数
-
可以通过空模板参数列表强制调用函数模板
-
函数模板可以发生函数重载
-
如果函数模板可以产生更好的匹配,优先调用函数模板
既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
1.2.5 模板的局限性
模板的通用性并不是万能的
例如,如果传入的两个数组类型的变量,无法进行赋值操作,又例如,传入自定义类型变量,无法进行比较运算
C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
利用具体化的模板,可以解决自定义类型的通用化
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
1.3 类模板
作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
语法:
template——声明创建模板
typename——表明其后面的符号是一种数据类型,可以用class代替
T ——通用的数据类型,名称可以替换,通常为大写字母
1.3.1 类模板基本语法
类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
1.3.1 类模板与函数模板区别
-
类模板没有自动类型推导的使用方式
-
类模板在模板参数列表中可以有默认参数
1.3.2 类模板中成员函数的创建时机
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
1.3.3 类模板对象做函数参数
类模板实例化出的对象,向函数传参的方式:
1.指定传入类型——直接显示对象的数据类型 (最常用)
2.参数模板化——将对象中的参数变为模板进行传递
3.整个类模板化——将这个对象类型模板化进行传递
1.3.4 类模板与继承
当类模板碰到继承时,需要注意:
-
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
-
如果不指定,编译器无法给子类分配内存
-
如果想灵活指定出父类中T的类型,子类也需变为类模板
1.3.5 类模板成员函数类外实现
1.3.5 类模板分文件编写
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
1.直接包含.cpp源文件(少用)
2.将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制(主流)
1.3.6 类模板与友元
类模板配合友元函数的类内和类外实现
- 全局函数类内实现——直接在类内声明友元即可
- 全局函数类外实现——需要提前让编译器知道全局函数的存在
建议全局函数做类内实现,用法简单,而且编译器可以直接识别
2、STL
- 长久以来,软件界希望建立一种可重复利用的东西
- C++的面向对象和泛型编程思想,目的就是复用性的提升
- 为了建立数据结构和算法的一套标准,诞生了STL
2.1 STL基本概念
- STL(Standard Template Library)标准模板库
- STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用了模板类或者模板函数
2.2 STL六大组件
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
- 算法:各种常用的算法,如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器(配接器):一种用来修饰容器或者仿函数或迭代器接口的东西
- 空间配置器:负责空间的配置与管理
2.3 STL中容器、算法、迭代器
每个容器都有自己专属的迭代器,迭代器使用非常类似于指针
迭代器种类
常用的容器中迭代器种类为双向迭代器和随机访问迭代器
2.4 STL初识
2.4.1 vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector< int >::iterator
2.4.2 vector存放自定义数据类型
要知道*it代表什么
如果是person,就是对象
如果是person *,就是指针
2.4.3 vector容器嵌套容器
2.5 STL常用容器
2.5.1 string容器
2.5.1.1 string基本概念
本质:string是C++风格的字符串,而string本质上是一个类
string和char*的区别:
- char*是一个指针
- string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器
特点:
string类内部封装了很多成员方法
例如:查找find,拷贝copy,删除delete,替换replace,插入insert
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
2.5.1.2 string构造函数
2.5.1.3 string赋值操作
string的赋值方式很多,operator=这种方式是比较实用的
2.5.1.4 string字符串拼接
2.5.1.5 string查找和替换
find查找是从左往后,rfind从右往左
find找到字符串后返回查找的第一个字符位置,找不到返回-1
replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
2.5.1.6 string字符串比较
2.5.1.7 string字符存取
string字符串中单个字符存取有两种方式,利用[ ]或at
2.5.1.8 string插入和删除
2.5.1.9 string子串
2.5.2 vector容器
功能:vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别:
不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
vector容器的迭代器是支持随机访问的迭代器
2.5.2.1 vector构造函数
2.5.2.2 vector赋值操作
vector赋值方式比较简单,使用operator=,
2.5.2.3 vector容量和大小
2.5.2.4 vector插入和删除
2.5.2.5 vector数据存取
2.5.2.6 vector互换容器
swap可以使两个容器互换,可以达到实用的收缩内存效果
2.5.2.7 vector预留空间
2.5.3 deque容器
2.5.3.1 deque容器基本概念
双端数组,可以对头端进行插入删除操作
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区发地址,使得使用deque时像一片连续的内存空间
2.5.3.2 deque容器构造函数
2.5.3.3 deque赋值操作
2.5.3.4 deque大小操作
- deque没有容量的概念
2.5.3.5 deque插入和删除
插入和删除提供的位置是迭代器
2.5.3.6 deque数据存取
2.5.3.7 deque排序
2.5.4 stack容器
2.5.4.1 stack基本概念
stack是一种先进后出的数据结构,它只有一个出口
外界只能访问栈顶的元素,因此栈不允许有遍历行为
栈可以判断容器是否为空
栈可以返回元素个数
- 入栈:栈中进入数据
- 出栈:栈中弹出数据
2.5.4.2 stack常用接口
2.5.5 queue容器
2.5.5.1 queue基本概念
queue是一种先进先出的数据结构,它有两个出口
- 队列容器允许从一端新增元素,从另一端移除元素
- 队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
入队:队列中进数据
出队:队列中出数据
2.5.5.2 queue常用接口
2.5.6 list容器
2.5.6.1 list基本概念
功能:将数据进行链式存储
链表是一种物理存储单元上非连续的存储结构,数据结构的逻辑顺序是通过链表中的指针链接实现的
- 链表的组成:链表是由一系列结点组成
- 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
- STL中的链表是一个双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
- 链表灵活,但是空间和时间额外耗费较大
list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的
STL中list和vector是两个最常被使用的容器,各有优缺点
2.5.6.2 list构造函数
list构造方式同其他几个STL常用容器,熟练掌握即可
2.5.6.3 list容器赋值与交换
2.5.6.4 list大小操作
2.5.6.5 list插入和删除
2.5.6.6 list数据存取
不可以用[ ]形式或者at访问list容器中的元素,原因是list本质是链表,不是用连续线性空间存储数据,迭代器也是不支持随机访问的(不能跳跃式访问)
2.5.6.7 list反转和排序
- 对于自定义数据类型,必须要指定排序规则,否则编译器不知道如何进行排序
- 高级排序只是在排序规则上再进行一次逻辑规则制定,并不复杂
2.5.7 set容器
2.5.7.1 set容器基本概念
所有元素都会在插入时自动被排序
本质:set/multiset属于关联式容器,底层结构是用二叉树实现
set和multiset区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
2.5.7.2 set构造及赋值
- set容器插入数据时用insert
- set容器插入数据的数据会自动排序
2.5.7.3 set大小和交换
2.5.7.4 set插入和删除
2.5.7.5 set查找和统计
- find——返回的是迭代器
- count——对于set结果为0或者1
2.5.7.6 set和multiset区别
区别:
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
- multiset不会检测数据,因此可以插入重复数据
2.5.7.7 pair对组创建
成对出现的数据,利用对组可以返回两个数据
2.5.7.8 set容器排序
利用仿函数,可以改变排序规则
1.set存放内置数据类型
利用仿函数指定排序规则
2.set存放自定义数据类型
对于自定义数据类型,set必须要指定排序规则才可以插入数据
2.5.8 map容器
2.5.8.1 map容器基本概念
- map中所有元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器,底层结构是用二叉树实现
优点:
-
可以根据key值快速找到value值
map和multimap区别: -
map不允许容器中有重复key值元素
-
multimap允许容器中有重复key值元素
2.5.8.2 map构造和赋值
map中所有元素都是成对出现,插入数据时候要使用对组
2.5.8.3 map大小和交换
2.5.8.4 map插入和删除
2.5.8.5 map查找和统计
2.5.8.6 map容器排序
- 利用仿函数,可以改变排序规则
2.5.9 容器比较