C++ 模板与STL

一、类型参数
x = 10;
x = "hello";
int x;
cin >> x;
double y;
Student s (...);
cin >> s;
1.为同一种算法,定义适用于不同类型的版本。
2.借助参数宏摆脱类型的限制,同时也丧失了类型安全。
3.让预处理器根据一个通用的“模板“宏生成针对不同类型的版本。
4.编写带有参数化类型的通用版本,让编译器自动生成针对不同类型的具体版本——模板机制。
1)模板函数(函数模板):带有类型参数的函数。函数的返回值、参数以及局部变量均可使用类型参数。函数模板支持隐式推断。
2)模板类(类模板):带有类型参数的类。类的成员变量、成员函数、成员类型以及基类均可使用类型参数。类模板不支持隐式推断。
3)模板的实例化:编译器用类型实参(显式/隐式)匹配模板中的类型形参后,生成的具体类或函数的过程,称为模板的实例化。
类模板 -实例化-> 类 -实例化-> 对象
            编译期           运行期
二、模板特化
1.可以为模板提供一种特殊化定义,作为一般情况之外的特例,为编译器提供一种更为合适的选择。
2.对于类模板而言,既可以做全类特化,也可以只针对部分与类型相关的成员函数进行特化。对于后者一定要保证特化版本的接口规格与通用版本完全一致。
三、模块划分和编译模型
1.包含模型:在模板的声明文件的末尾包含该模板的实现。强制将模板的定义和实例化放在同一个编译单元中。
1)问题一:模板的实现部分必须对用户公开;
2)问题二:增加编译时间。
2.导出模型:在模板声明之前加上export关键字,表示该模板将在定义模块之外被实例化。
export template< ... >
void foo (void) { ... }
export template< ... >
class A { ... };
问题:几乎没有编译器支持这么用(只有Edison Design Group, Inc.公司的编译器支持)。
C++2011中已经放弃了该特性。
四、局部特化
1.对于具有多个类型参数的类模板,可以只特化其中的一部分类型参数。编译器优先选择特化程度最高的版本。
2.对于具有多个类型参数的类模板,可以针对类型参数之间某种特殊关系进行特化。编译器优先选择匹配程度最高的版本。
3.对于类模板,可以针对指针或者数组进行特化。编译器会优先选择针对指针或数组的特化。
4.函数模板不能做局部特化,只有类模板可以。
5.反之由局部特化引起的实例化匹配冲突。
五、非类型参数
1.除了类型参数以外,模板也可以接受非类型参数。
2.传递给模板的非类型参数只能是常量、常量表达式或者具有常属性(const)的变量,但是不能被volatile(挥发性饰词)修饰。
3.函数模板和类模板都可以带有非类型参数。
六、缺省参数
1.无论是类型参数还是非类型参数都可以带有缺省值。如果实例化模板是没有提供相应的模板实参,该参数就取缺省值。
2.如果某一个模板参数带有缺省值,那么该参数后面的所有参数必须都带有缺省值。
3.后面参数的缺省值可以引用前面参数的值。
4.C++98中只有类模板可以带有缺省参数,函数模板不能带有缺省参数(避免和隐式推断混淆)。
七、typename和class
class          - 表示类
                  - 表示模板的类型参数
typename - 表示模板的类型参数
                  - 表示依赖类型
八、其它细节
1.从基模板继承
2.模板型模板参数
3.模板的模板成员
4.模板的递归实例化
A<A<int> >
Array<List<int>, 10>
List<Array<int, 10> >

List<list<int> >

一、容器和迭代器
1.容器:泛型化的数据结构,通过一套统一的标准接口为用户提供不同的逻辑功能。如QList,list等。
2.迭代器:通过一个抽象类型,使用户可以通过一组一致且透明的方法,以统一的方式访问不同容器中的数据,为泛型算法提供支持。
二、案例:链表
1.直接构造空链表
2.支持深拷贝的拷贝构造和拷贝赋值
3.提供一组操作接口,如:
front/push_front/pop_front
back/push_back/pop_back
等等
4.支持内置迭代器类型,以及基于迭代器的插入和删除操作
List list;
...
const List& cr = list;
cr.front
3 4 5
Node* next (Node* p) {
}
Node* prev (Node* p) {
}
Node* p = next (NULL);
p = next (p);
p = next (p);
int a[5] = {1, 2, 3, 4, 5};
int* p = a;
*p
++p;
--p;
const int* cp = a;
++cp;
*cp = 100; // ERROR;
int *const pc;
const int* cp;
int* p = cp;
三、STL概述
1.标准模板库主要包括三个部分
1)容器:存储和管理对象的集合。数据结构的泛型化封装。
2)迭代器:在不暴露容器内部实现细节的前提下,为用户提供访问容器中数据元素的统一方法。
3)泛型算法:借助于迭代器,以泛型的方式操作容器中的数据元素。
2.STL的所有组件都是通过模板定义的。支持对数据类型的泛化。
3.STL所强调的是将数据结构和算法与具体的数据类型独立开来。
4.STL所追求的是在尽量小的框架内实现最大的弹性。
四、十大容器
1.线性容器:向量(vector)、列表(list)、双端队列(deque)
2.适配器容器:堆栈(stack)、队列(queue)、优先队列(priority_queue)
3.关联容器:映射(map)、多重映射(multimap)、
集合(set)、多重集合(multiset)
五、STL容器的共性
1.所有的STL容器都提供支持深拷贝的拷贝构造函数和拷贝赋值运算符函数。但是容器中的元素类型也需要支持深拷贝。
2.容器之间可以进行相等和不等的判断。容器判等的条件是:容器和元素类型都相同,容器中元素的个数相同,对应元素之间满足“==”运算。
3.容器中保存的永远是被放入对象的副本,而非其本身。
4.容器中不要放入auto_ptr。
六、向量
1.基本特性
1)用连续的地址空间存放数据元素,因此支持通过下标随机访问其中的元素。
2)动态内存管理。随着新元素的不断加入,其内存空间自动增长(但是不能自动收缩)。
3)通过预分配空间降低动态内存管理的开销。
4)支持在随机位置的插入和删除,但是只有在接近容器尾端进行这两个操作效率才比较高。
2.实例化
#include <vector>
1)空向量
vector<元素类型> 向量对象;
vector<int> vi;
2)指定初始大小
vector<元素类型> 向量对象 (初始大小);
vector<int> vi (10);
基本类型:用0初始化。
类类型:用缺省构造初始化。
3)在指定初始大小的同时也指定初值
vector<元素类型> 向量对象 (初始大小, 初值);
vector<int> vi (10, 5);
4)通过其它容器初始化
int a[5] = {12, 23, 44, 18, 37};
vector<int> vi (&a[0], &a[5]);
vector<int> vi (a, a + 5);
3.迭代器
1)四个迭代器
正向迭代器:iterator
常正向迭代器:const_iterator
反向迭代器:reverse_iterator
常反向迭代器:const_reverse_iterator
2)随机迭代器相对于顺序迭代器而言,增加以下功能:
和整数的加减运算;
同型迭代器之间的比较和相减运算。
4.常用成员函数
value_type& front (void);
const value_type& front (void) const;
value_type& back (void);
const value_type& back (void) const;
void push_back (const value_type& val);
void pop_back (void);
iterator insert (iterator loc,
  const value_type& val);
iterator erase (iterator loc);
iterator begin (void);
const_iterator begin (void) const;
iterator end (void);
const_iterator end (void) const;
reverse_iterator rbegin (void);
const_reverse_iterator rbegin (void) const;
reverse_iterator rend (void);
const_reverse_iterator rend (void) const;


5.注意迭代器的有效期。
int* p = new int[10];
// ...
delete[] p;
任何可能导致容器结构发生变化的函数(push_back/insert/pop_back/erase/sort等等)被调用,先前获得的迭代器都可能因此而失效,重新初始化以后再使用才是安全的。
6.大小和容量
大小:实际容纳元素的个数。
容量:最多容纳元素的个数。
size_type size (void) const;
获取大小
void resize (size_type num, const value_type& val = value_type ());
改变大小,可增可减,增构造,减析构,第二个参数表示新增元素的初始值,不指定第二个参数就用缺省方式初始化(0/缺省构造)
void clear (void);
清空,相当于resize (0)
bool empty (void) const;
判空,空则返回true,否则返回false
size_type capacity (void) const;
获取容量
void reserve (size_type size);
改变容量,只增不减,新增部分不做初始化
对于容量以内,大小以外的元素可以访问,但其值并不稳定,随时可能因后续操作而发生改变。
class Student {
  char name[256];
  int age;
  char info[1024];
};
(1024+4+256)*100
class Student {
  string name;
  int age;
  string info;
};
(4+4+4)*100
无论是通过成员函数减少向量中的元素,还是通过reserve()直接修改向量的容量,容量都是只能增加不能减少的。因此,对于持久化的向量容器,不适合存放太多的大对象。如果元素对象需要比较多的内存资源,建议采用动态分配的方式,在构造和析构函数中自己管理,或者使用string等其它容器自动管理。
7.查找和排序
#include <algorithm>
iterator find (iterator begin, iterator end,
  const value_type& val);
返回从begin开始到end之前的元素为止,第一个与val匹配的元素的迭代器。查找失败返回end。
void sort (iterator begin, iterator end);
从begin开始到end之前的元素为止,用快速排序算法进行排序。
void sort (iterator begin, iterator end,
  less cmp);
cmp:比较器(用函数指针/函数对象实现比较规则)
8.由类类型对象组成的容器
1)缺省构造函数
2)支持深拷贝的拷贝构造函数和拷贝赋值运算符函数
A a;
A* p = &a;
A& r = a;
3)如果需要查找,那么还应该支持“==”运算符
4)如果需要排序,那么还应该支持“<”运算符,或者提供比较器
一、字符串
#include <string>
template<typename T, ...>
class basic_string { ... };
typedef basic_string<char> string;
ASCII/GBK/BIG5/UTF-8
typedef basic_string<wchar_t> wstring;
UCS-2/UCS-4
我学C语言
iconv
1.实例化
string str;
string str ("");
string str = "";
string str ("hello, world");
string str = "hello, world";
string str = string ("");
string str = string ("hello, world");
string str (); // 错误!
string str = string (); // 错误!
string* str = new string;
string* str = new string ();
string* str = new string ("hello, world");
2.转换
C->C++
char cs[] = "hello, world";
string cpps (cs);
string cpps = cs;
string cpps; cpps = cs;
C++->C
string cpps ("hello, world");
printf ("%s\n", cpps.c_str ());
strlen (cpps.c_str ());
3.计算
拼接:+
赋值:=/+=
比较:</<=/>/>=/==/!=
下标:[]
输入输出:<</>>(VC6不支持)
4.大小和容量
size:获取字符数,不包括结尾空字符
resize:改变大小
clear:清空
empty:判断是否为空串
capacity:获取容量
reserve:改变容量,只增不减
length:获取字符串长度,同size
5.拼接
+/+=
string& append (const string& str);
string& append (const string& str,
  size_type pos, size_type len);
string& append (size_type num, char ch);
6.搜索
size_type find_first_of (char ch,
  size_type pos = 0);
size_type find_first_of (const string& str,
  size_type pos = 0);
返回调用字符串中从pos处开始第一个出现在参数str字符串中的字符的下标
size_type find_first_not_of (char ch,
  size_type pos = 0);
size_type find_first_not_of (const string& str,
  size_type pos = 0);
size_type find_last_of (char ch,
  size_type pos = 0);
size_type find_last_of (const string& str,
  size_type pos = 0);
size_type find_last_not_of (char ch,
  size_type pos = 0);
size_type find_last_not_of (const string& str,
  size_type pos = 0);
7.查找
size_type find (const string& str,
  size_type pos);
size_type rfind (const string& str,
  size_type pos);
8.替换
string& replace (size_type pos, size_type len,
  const string& str);
9.访问单个字符
[] - 不检查下标溢出
char& at (size_type index); - 下标溢出抛异常
const char& at (size_type index) const;

一、双端队列(deque)
1.双端队列的物理结构与向量几乎完全一样,唯一的区别就是双端队列的首尾两头都是开放的,都可以压入和弹出数据。
2.接口与向量相比增加了push_front/pop_front函数,去掉了capacity/reserve函数。
3.性能方面双端队列比向量略差一点。内存消耗比向量多。对元素的访问时间也比向量略长。
4.空间的对称性决定了双端队列在首尾两端做同样操作的时间复杂度也是对称的。
二、列表(list)
1.列表是按照链式线性表(链表)的形式进行存储的。
2.在列表的任意位置插入/删除都是常数时间的。
3.不支持下标运算符,也不支持随机迭代。
4.常用成员函数
front/push_front/pop_front
back/push_back/pop_back
insert/erase
size/resize/clear/empty
begin/end/rbegin/rend
void remove (const value_type& val);
删除所有匹配元素。
void unique (void);
唯一化相邻重复元素。
void splice (iterator pos, list& lst);
将参数列表的全部元素剪切到调用列表pos处。
void splice (iterator pos, list& lst,
  iterator del);
将参数列表中del处的元素剪切到调用列表pos处。
void splice (iterator pos, list& lst,
  iterator begin, iterator end);
将参数列表中从begin开始到end之前为止的元素剪切到调用列表的pos处。
void sort (void);
void sort (less cmp);
排序。
void merge (list& lst); // 通过“<”比大小
void merge (list& lst, less cmp); // 通过cmp
                                                      // 比大小
将有序参数列表中的全部元素剪切到有序调用列表中,并保证调用列表依然有序。
三、堆栈(stack)
后进先出
push    -> push_back
pop      -> pop_back
top       -> back
size       -> size
empty   -> empty
stack<元素类型, 底层容器类型> 堆栈对象;
stack<int, vector<int> > si;
缺省底层容器是deque。
四、队列(queue)
先进先出
push    -> push_back
pop      -> pop_front
front    -> front
back     -> back
size       -> size
empty   -> empty
不能用向量(vector)作为队列的底层容器。
缺省底层容器是deque。
五、优先队列(priority_queue)
优者先出
push - 压入
pop - 弹出
top - 队首
priority_queue<元素类型, 底层容器类型>
用元素类型的“<”比大小,以大者为优。
priority_queue<元素类型, 底层容器类型, 比较器类型>
通过比较器比大小,以大者为优。
底层容器可以使用vector或deque(缺省)。
六、映射(map)
1.映射是一个key-value对的序列,其中每个key都是唯一的。
2.存储方面,映射采用的是平衡有序二叉树的结构。
3.key的大小比较可以通过其类型的“<”运算符,或者比较器完成。
4.映射中的数据元素以pair为单位进行存储和访问。pair有两个成员变量,一个叫first对应映射中的key,另一个叫second对应映射中的value。
5.映射支持顺序迭代,对映射的迭代采用的是中序遍历,所得到的是关于key的有序序列。
6.支持以key为参数的下标运算符,获取/添加与该key对应的value。
字符表示key,候选人对象表示value,构成map。
pair<iterator, bool> insert (conat pair<key_type, value_type>& pair);
七、多重映射(multimap)
1.允许key重复,一个key对映多个value。
A - 100
A - 200
A - 300
2.不支持下标运算符。
3.常用成员函数
iterator lower_bound (const key_type& key);
获取匹配下限,失败返回终止迭代器。
iterator upper_bound (const key_type& key);
获取匹配上限,失败返回终止迭代器。
A - 100
B - 200 <- lower_bound ('B');
B - 300
B - 400
C - 500 <- upper_bound ('B');
pair<iterator, iterator> equal_range (const key_type& key);
同时返回匹配上下限。
八、集合(set)
没有value的映射。
九、多重集合(multiset)
允许包含重复元素的集合。
没有value的多重映射。
iterator lower_bound (const key_type& key);
获取匹配下限,失败返回终止迭代器。
iterator upper_bound (const key_type& key);
获取匹配上限,失败返回终止迭代器。
A
B <- lower_bound ('B');
B
B
C<- upper_bound ('B');
pair<iterator, iterator> equal_range (const key_type& key);
同时返回匹配上下限。
十、泛型算法
以迭代器为媒介可以处理不同容器中不同类型数据元素的通用算法函数。




































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值