一、学习内容
C++ 标准模板库STL
标椎模板类对数据元素按照泛型方式处理。
1、概述
- STL由一些可适应不同需求的集合类(collection class),以及在这些数据集合上操作的算法(algorithm)构成
- STL内的所有组件都由模板(template)构成,其元素可以是任意类型
组件:
STL容器类别
- 序列式容器-排列次序取决于插入时机和位置
- 关联式容器-排列顺序取决于特定准则
2、STL容器的共同操作
初始化:根据数据类型定义对象
元素操作
指针指向元素
(1)迭代器
迭代器对象必须与容器类型一致
例:
vector<char>::iterator p = v.begin();//定义迭代器p初始化为容器起始位置
p++;//将迭代器移向下一个位置
cout << *p << endl;
p++;
cout << *p << endl;
p--;//移向上一个位置
cout << *(p + 2) << endl;//输出第四个元素
迭代器示例:iterator
存储方式不同,操作方式相同
例:
list<int> l;
for(pos=l.begin();pos!=l.end();++pos{
…
}
vector<int> v;
for(pos=v.begin();pos<v.end();++pos{
…
}
(2)vector(序列式容器)
vector模拟动态数组
特点:
取代数组,容量可变;
有一组操作函数。
- 必须包含的头文件
#include <vector>
- vector的大小(size)和容量(capacity)
例:
vector<string> sentence;
sentence.reserve(5);
sentence.push_back(“hello”);
sentence.push_back(“world”);
cout << “max_size():” << sentence.max_size() << endl;
cout << “size():” << sentence.size() << endl;
cout << “capacity():” << sentence.capacity() << endl;
操作
例:
vector<int> a; //声明一个int型的向量a;
vector<int> a(10); //声明一个初始大小为10的向量a;
vector<int> a(10,1); //声明一个初始大小为10且初始值都未1的向量a;
vector<int> b(a); //声明并用向量a初始化b;
vector<int> b(a.begin(),a.begin()+3);
//声明并将向量a的第0个到第2个(共3个)作为向量b的初始值
例:
vector<int> a(10) ; //大小为10的向量a
a.insert(1,3);//在第1个位置插入3
cout<<a.size()<<endl;//1
cout<<a.empty()<<endl;//ture
cout<<c.capacity();//10
a.reserve(15);//扩容为15
cout<<<c.capacity();//15
例:
std::list<T> l;
std::vector<T> v;
…
v.assign(l.begin(),l.end());
例:
std::vector<T> v;//empty
v[5]= t; //runtime error
std::cout << v.front(); //runtime error
//----------------------------
vector<int> v;
v.reserve(10);
for(int i=0; i<7; i++) {
v.push_back(i); //在V的尾部加入7个数据
}
try {int iVal1 = v[7];
// not bounds checked - will not throw
int iVal2 = v.at(7);
// bounds checked - will throw if out of range
}
catch(const exception& e) {
cout << e.what();
}
例:
vector<int> a;
for (int i = 0; i < 5; ++i){
a.push_back(5 - i); // 每次在末尾增加一元素
}
cout << a.size() << endl;//5
例:
vector<int> a;
for (int i = 0; i < 5; ++i){
a.push_back(5 - i); // 每次在末尾增加一元素
}
cout << a.size() << endl;
a.pop_back(); //删最后一元素
a[0] = 1; //向量可做数组使用
cout << a.size() << endl;
for (int i = 0; i < (int)a.size(); ++i){ //类型转换
cout << a[i] << ", " << endl
}
sort(a.begin(), a.end());
cout << "Size: " << a.size() << endl;
for (int i = 0; i < (int)a.size(); ++i){
cout << a[i] << ", " << endl;//5432
}
cout << endl;
a.clear();
cout << "Size: " << a.size() << endl;
(3)map/multimap
通过map容器对向量输入的数据通过关键字索引,并且可用map存储
内部存储结构
特殊搜寻操作
例:
map
struct T1{
int v;
bool operator<(const T1 &a)const{
return (v < a.v);
}
};
struct T2{
int v;
};
struct cmp{
const bool operator()(const T2 &a, const T2 &b){
return (a.v < b.v);
}
};
int main(){
map<T1, int>mt1; //example for user-defined class
map<T2, int, cmp>mt2; //example for user-defined class(functor)
map<string, int> m2;
map<string, int>::iterator m2i, p1, p2;
m2["abd"] = 2;
m2["abc"] = 1;
m2["cba"] = 2;
m2.insert(make_pair("aaa", 9));
m2["abf"] = 4;
m2["abe"] = 2;
cout << m2["abc"] << endl;
m2i = m2.find("cba");
if(m2i != m2.end()){
cout << m2i->first << ": " << m2i->second << endl;
}else{
cout << "find nothing" << endl;
}
cout << "Iterate" << endl;
for(m2i = m2.begin(); m2i != m2.end(); m2i++){
cout << m2i->first << ": " << m2i->second << endl;
}
}
multimap
multimap<string, int> mm1;
multimap<string, int>::iterator mm1i, p1, p2;
mm1.insert(make_pair("b", 3));
mm1.insert(make_pair("a", 0));
mm1.insert(make_pair("b", 5));
mm1.insert(make_pair("c", 4));
mm1.insert(make_pair("b", 2));
cout << mm1.size() << endl;
for(mm1i = mm1.begin(); mm1i != mm1.end(); mm1i++){
cout << mm1i->first << ": " << mm1i->second << endl;
}
cout << "COUNT: " << mm1.count("b") << endl;
cout << "Bound: " << endl;
p1 = mm1.lower_bound("b");
p2 = mm1.upper_bound("b");
for(mm1i = p1; mm1i != p2; mm1i++){
cout << mm1i->first << ": " << mm1i->second << endl;
}
(4)set/multiset
操作
例:
set
struct T1{
int key;
int value1, value2;
bool operator<(const T1 &b)const{
return (key < b.key);
}
};
struct T2{
int key;
int v1, v2;
};
struct T2cmp{
bool operator()(const T2 &a, const T2 &b){
return (a.key < b.key);
}
};
int main(){
set<T1> s2;
set<T2, T2cmp> s3;
#if 1
set<string>s1;
set<string>::iterator iter1;
#else
set<string, greater<string> >s1;
set<string, greater<string> >::iterator iter1;
#endif
s1.insert("abc");
s1.insert("abc");
s1.insert("abc");
s1.insert("bca");
s1.insert("aaa");
cout << "ITERATE:" << endl;
for (iter1 = s1.begin(); iter1 != s1.end(); iter1++){
cout << (*iter1) << endl;
}
cout << "FIND:" << endl;
iter1 = s1.find("abc");
if(iter1 != s1.end()) {
cout << *iter1 << endl;
}else{
cout << "NOT FOUND" << endl;
}
return 0;
}
multiset
int main(){
multiset<T1> s2;
multiset<T2, T2cmp> s3;
#if 1
multiset<string>s1;
multiset<string>::iterator iter1;
#else
multiset<string, greater<string> >s1;
multiset<string, greater<string> >::iterator iter1;
#endif
s1.insert("abc");
s1.insert("abc");
s1.insert("abc");
s1.insert("bca");
s1.insert("aaa");
cout << "ITERATE:" << endl;
for (iter1 = s1.begin(); iter1 != s1.end(); iter1++)
cout << (*iter1) << endl;
cout << "FIND:" << endl;
iter1 = s1.find("abc");
if(iter1 != s1.end())
cout << *iter1 << endl;
else cout << "NOT FOUND" << endl;
cout << s1.count("abc") << endl;
multiset<string>::iterator s1i, p1, p2;
p1 = s1.lower_bound("abc");
p2 = s1.upper_bound("abc");
for(s1i = p1; s1i != p2; s1i++){
cout << (*s1i) << endl;
}
return 0;
}
pair 模板:
pair模板可以用于生成 key-value对
例:
typedef set<double,less<double> > double_set;
const int SIZE = 5;
double a[SIZE] = {2.1,4.2,9.5,2.1,3.7 };
double_set doubleSet(a,a+SIZE);
ostream_iterator<double> output(cout," ");
cout << "1) ";
copy(doubleSet.begin(),doubleSet.end(),output);
cout << endl;
pair<double_set::const_iterator, bool> p;
p = doubleSet.insert(9.5);
if( p.second )
cout << "2) " << * (p.first) << " inserted" << endl;
else
cout << "2) " << * (p.first) << " not inserted" << endl;
insert函数返回值是一个pair对象, 其first是被插入元素的迭代器,second代表是否成功插入了
输出:
- 2.1 3.7 4.2 9.5
- 9.5 not inserted
(5)Algorithm(算法)
- 泛型算法通则
- count:
size_t count(InIt first, InIt last, const T& val); //计算[first,last) 中等于val的元素个数
- count_if
size_t count_if(InIt first, InIt last, Pred pr); //计算[first,last) 中符合pr(e) == true 的元素 e的个数
- min_element:
template<class FwdIt> FwdIt min_element(FwdIt first, FwdIt last); // 返回[first,last) 中最小元素的迭代器,以 “< ”作比较器
-max_element:
template<class FwdIt> FwdIt max_element(FwdIt first, FwdIt last); //返回[first,last) 中最大(不小)元素的迭代器,以 “< ”作比较器
- for_each
template<class InIt, class Fun> Fun for_each(InIt first, InIt last, Fun f); //对[first,last)中的每个元素 e ,执行 f(e) , 要求 f(e)不能改变e
class CLessThen9 {
public:
bool operator()( int n) { return n < 9; }
};
void outputSquare(int value ) { cout << value * value << " "; }
}
int main() {
const int SIZE = 10;
int a1[] = { 100,2,8,1,50,3,8,9,10,2 };
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");
cout << endl << "2)";
cout << count(v.begin(),v.end(),8);
cout << endl << "3)";
cout << count_if(v.begin(),v.end(),CLessThen9());
cout << endl << "4)";
cout << * (min_element(v.begin(),v.end()));
cout << endl << "5)";
cout << * (max_element(v.begin(),v.end()));
cout << endl << "7) ";
for_each(v.begin(),v.end(),outputSquare);
输出:
2)2
3)6
4)1
5)100
7) 10000 4 64 1 2500 9 64 81 100 4
排序和查找算法
例:
bool Greater10(int n)
{
return n > 10;
}
main() {
const int SIZE = 10;
int a1[] = { 2,8,1,50,3,100,8,9,10,2 };
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");
vector<int>::iterator location;
location = find(v.begin(),v.end(),10);
if( location != v.end()) {
cout << endl << "1) " << location - v.begin();
}
location = find_if( v.begin(),v.end(),Greater10);
if( location != v.end())
cout << endl << "2) " << location - v.begin();
sort(v.begin(),v.end());
if( binary_search(v.begin(),v.end(),9)) {
cout << endl << "3) " << "9 found";
}
}
输出:
- 8
- 3
- 9 found
二、注意事项
- 容器类型都是泛型,元素类型要具体化
- STL区间均为左闭右开
半开区间[beg, end)的好处:
1.为遍历元素时循环的结束时机提供了简单的判断依据(只要未到达end(),循环就可以继续)
2.不必对空区间采取特殊处理(空区间的begin()就等于end()) - 迭代器持续有效,除非发生以下两种情况:
(1)删除或插入元素
(2)容量变化而引起内存重新分配 - STL容器元素的条件
必须能够通过复制构造函数进行复制
必须可以通过赋值运算符完成赋值操作
必须可以通过析构函数完称销毁动作
序列式容器元素的默认构造函数必须可用
某些动作必须定义operator ==,例如搜寻操作
关联式容器必须定义出排序准则,默认情况是重载operator <
三、学习感受
最近开始了图书管理-后台的作业,需要用到STL方面的知识。一开始在并没有很熟练的情况下,发现自己真的是干啥啥不会,心态比较崩。代码出错的时候,就感觉自己陷入到了一个沼泽地,越改越错,通常一坐好几个小时就过去了,就很愁,特别愁,非常愁。然后我就去找百度,看关于模板库方面的资料,做好充足的准备工作后再投入到我的“编程大业”中,这时会比一开始的状态好很多很多。好像每一次做作业的过程都比较曲折,但好在运行出结果的时候,总是会恍然大悟,会很激动,然后带着这份激动继续投入到我的下一次作业中。因为刚开始敲代码比较多的这种编程作业,也在不断的适应。最后感觉自己在这一阶段的学习中跟着老师有收获到了不少,不只是在编程方面,还有在学习方面的,然后希望在往后的日子里,自己能够更加勤奋,敲代码的速度越来越快,然后编程能力越来越好。