1 2 3 4

c++泛型编程与STL

本文主要针对c++泛型编程 和 STL

相关知识:

c++基础

c++面向对象OOP

 

1. 模板

  • C++另一种编程思想称为 ==泛型编程== ,主要利用的技术就是模板(可能会造成代码膨胀)

  • C++提供两种模板机制:函数模板( template )类模板

 

普通函数与函数模板区别: 

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)

  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

  • 如果利用显示指定类型的方式,可以发生隐式类型转换

调用规则如下:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数

  2. 可以通过空模板参数列表来强制调用函数模板

  3. 函数模板也可以发生重载

  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性

类模板与函数模板区别主要有两点:

  1. 类模板只能用显示指定类型方式

  2. 类模板在模板参数列表中可以有默认参数

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建

  • 类模板中的成员函数在调用时才创建

当类模板碰到继承时:如果父类是类模板,子类需要指定出父类中T的数据类型

类模板分文件编写:将类模板成员函数写到一起,并将后缀名改为.hpp

全局函数类内实现 - 直接在类内声明友元即可

数组模板的实现

 1 #pragma once
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 template<class T>
 7 class MyArray {
 8 public:
 9     MyArray(int capacity) {
10         this->m_capacity = capacity;
11         m_size = 0;
12         this->pAddress = new T[this->m_capacity];
13     }
14 
15     MyArray(const MyArray& arr) {
16         this->m_capacity = arr.m_capacity;
17         this->m_size = arr.m_size;
18         this->pAddress = new T[this->m_capacity];
19         for (int i = 0; i < this->m_size; i++) {
20             this->pAddress[i] = arr.pAddress[i];
21         }
22     }
23 
24     MyArray& operator=(const MyArray& arr) {
25         if (this->pAddress != NULL) {
26             delete[] this->pAddress;
27             this->pAddress = NULL;
28             this->m_capacity = 0;
29             this->m_size = 0;
30         }
31 
32         this->m_capacity = arr.m_capacity;
33         this->m_size = arr.m_size;
34         this->pAddress = new T[this->m_capacity];
35         for (int i = 0; i < this->m_size; i++) {
36             this->pAddress[i] = arr.pAddress[i];
37         }
38 
39         return *this;
40     }
41 
42     T& operator [](int index) {
43         return this->pAddress[index];
44     }
45 
46     void push_back(const T& val) {
47         if (this->m_capacity == this->m_size) {
48             return;
49         }
50         this->pAddress[this->m_size] = val;
51         this->m_size++;
52     }
53 
54     void pop_back() {
55         if (this->m_size == 0) {
56             return;
57         }
58         this->m_size--;
59     }
60 
61     int getCapacity() {
62         return this->m_capacity;
63     }
64 
65     int getSize() {
66         return this->m_size;
67     }
68 
69     ~MyArray() {
70         if (this->pAddress != NULL) {
71             delete[] this->pAddress;
72             this->pAddress = NULL;
73             this->m_capacity = 0;
74             this->m_size = 0;
75         }
76         
77     }
78 
79 
80 private:
81     T* pAddress;
82     int m_capacity;
83     int m_size;
84 };
View Code

2. STL初识

面向对象和泛型编程的目的就是复用性的提升

  • STL(Standard Template Library,标准模板库)

  • STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)

  • 容器算法之间通过迭代器进行无缝连接。

  • STL 几乎所有的代码都采用了模板类或者模板函数

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。

  2. 算法:各种常用的算法,如sort、find、copy、for_each等

  3. 迭代器:扮演了容器与算法之间的胶合剂。

  4. 仿函数:行为类似函数,可作为算法的某种策略。

  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。

  6. 空间配置器:负责空间的配置与管理。

容器分为序列式容器关联式容器两种:

序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。

关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法分为:质变算法非质变算法

质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等

非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

迭代器:容器和算法之间粘合剂

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

每个容器都有自己专属的迭代器

迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

种类功能支持运算
输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写,支持++
前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--,
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、-n、<、<=、>、>=

常用的容器中迭代器种类为双向迭代器,和随机访问迭代器

3. STL 常用容器

3.1 string容器

string:是C++风格的字符串,而string本质上是一个类

string和char * 区别:

  • char * 是一个指针

  • string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。

特点:

string 类内部封装了很多成员方法

例如:查找find,拷贝copy,删除delete 替换replace,插入insert

string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

函数:

isdigit():数字

isalpha():字母

isupper():是否大写

islower():是否小写

tolower():转化为小写

toupper():转化为大写

 

isspace()    isblank(): 空格

ispunct():其他字符

isalnum():判断是否是数字和字母

tolower():转化为小写

 使用:

  • string构造函数
  • string赋值操作:=; assign()
  • string字符串拼接:+=; append()
  •  string 查找和替换:find(); rfind(); replace()
  • string 字符串比较:compare()
  • string 字符存取:[]; at()
  • string 插入和删除:insert(); erase()
  • string 字串:substr(0, k);

3.2.vector

单端数组,可以动态拓展(开辟更大的内存空间,拷贝原数据,释放原空间),支持随机访问。

  • 构造函数
  • 赋值操作 :=; assign()
  • 容量和大小:empty(); capacity(); size(); resize()
  • 插入和删除:push_back(); pop_back(); insert(); erase(); clear()
  • 数据存取:[]; at(); front(); back();
  • 互换容器: swap()
  • 预留空间:reserve()
  • 去重:unique(vec.begin, vec.end)
  • 删除:erase(unique(vec.begin, vec.end), vec.end)

3.3 deque

双端数组,降低了访问元素更慢,换来了头部的插入删除效率。支持随机访问。

原理:

deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据

 

中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

 

 

 

  •  构造,赋值,大小,数据存取的操作与 vector 一致
  • 没有容量的概念
  • 插入:多了头部的操作  push_front(); pop_front()

3.4 stack

栈,先进后出

 

常用接口:

  • 数据存取:push(); pop(); top()
  • 大小操作:empty(); size()

3.5 queue

队列,先进先出,

常用接口:

  • 数据存取:push(); pop(); back(); front()
  • 大小:empty(); size()

 

优先级队列:本质是堆,优先级高的先出。(最大堆,最小堆)

priority_queue,头文件<queue>

语法:priority_queue<Type, Continuer, Functional>

在STL中,默认情况(不加后两个参数)是以vector为容器,以operator< 为比较方式,即优先输出最大的元素。比较方式为greater<int>时,为小顶堆,less<int>为大顶堆(默认)头文件<functional>

成员函数:

函数 描述
push() 插入新元素
pop() 弹出
top() 取订
size() 大小
empty() 判空
swap() 交换元素
emplace() 构造并插入元素

 

3.6 list

链表,属于双向迭代器

常用接口:

  • 赋值和交换:assign(); =; swap()
  • 大小操作:size(); empty(); resize()
  • 插入和删除:push_back(); pop_back(); push_front(); pop_front(); insert(); clear(); erase(); remove()
  • 数据存取:front(); back()
  • 反转和排序:reverse(); sort()

sort()排自定义数据类型时,需要传入“比较器”。

3.7 set/ multiset

集合,所有元素在被插入时会自动排序,属于关联式容器,底层结构是二叉树

multiset 运行出现重复元素。

常用接口:

  • 插入与删除:insert(); clear(); erase()
  • 大小和交换:size(); empty(); swap()
  • 查找和统计:find(); count()

pair 对组创建:建议直接使用构造函数

set 默认从小到大排序,利用仿函数可以改变排序规则

3.8 map / multimap

map 中所有元素都是 pair ,第一个元素是 key, 第二个元素是 value。所有元素会根据 key 自动排序。

属于关联式容器,底层结构是二叉树。

常用接口:

  • 大小和交换:size(); empty(); swap()
  • 插入和删除:insert(); clear(); erase()
  • 查找和统计:find(); count()

4. STL- 函数对象

函数对象 概念:重载函数调用操作符的类,也叫仿函数

谓词:返回bool 类型的仿函数称为谓词,接收一个参数的叫一元谓词,接收两个参数的叫二元谓词。

内建函数对象(头文件:< functional>)分类:

  • 算术仿函数:实现四则运算
  • 关系仿函数:大于小于等于
  • 逻辑仿函数:与, 或, 非

5. STL- 常用算法

主要包含头文件

  • < algorithm >:比较,交换,查找,遍历,复制,修改
  • < functional >:定义了一些模板类
  • < numeric >:包含了在序列上进行简单数学运算的模板函数

算法:(随机数种子:srand((unsigned int)time(NULL)))

  • 遍历算法:for_each(); transform()
  • 查找算法:find(); find_if(); adjacent_find(); binary_search(); count(); count_if()
  • 排序算法:sort(); random_shuffle(); merge(); reverse()
  • 拷贝和替换算法:copy(); replace(); replace_if(); swap()
  • 算术生成算法:<numeric>    accumulate(); fill()
  • 集合算法:set_intersection(); set_union(); set_difference()

sort(vec.begin(), vec.end(), greater<int>())

 

posted @ 2021-11-28 16:01  木木木999  阅读(98)  评论(0)    收藏  举报