目录
1 STL---Standard Template Library---标准模板库
长久以来,软件界一直希望建立一种可重复利用的东西
C++的面向对象和泛型编程思想,目的就是复用性的提升;
为了建立数据结构和算法的一套标准,诞生了STL
1 STL---Standard Template Library---标准模板库
1.1 STL基本概念
(1)STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
(2)容器和算法之间通过迭代器进行无缝连接;
(3)STL几乎所有代码都采用了模板类或者模板函数;
1.2 STL六大组件
STL大体分为六大组件:分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
1、容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据;
2、算法:各种常用算法:如sort,find,coopy,for_each等;
3、迭代器:扮演了容器和算法的胶合剂;
4、仿函数:行为类似函数,可作为算法的某种策略;
5、适配器:一种用来修饰容器或者仿函数或迭代器接口的东西;
6、空间适配器:负责空间的配置与管理。
1.3 STL中容器、算法、迭代器
1、容器:置物之所也
(1)STL容器是将运用最广泛的一些数据结构实现出来;
(2)常用的数据结构:数组、链表、树、栈、队列、集合、映射表等;
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置;
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系;
2、算法:问题之解法也
(1)有限的步骤,解决逻辑或数学上的问题,这一门学科叫做算法(Algorithms)
(2)算法分为质变算法和非质变算法:
质变算法:是指运算过程中会更改区间内的元素的内容,例如拷贝、替换,删除等;
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等;
3、迭代器:容器和算法之间的粘合剂
提供一种方法,是指能够依序某个容器所含的各个元素,而又无需暴露该容器的内部表示方式;
每个容器都有自己专属的迭代器;迭代器使用非常类似于指针;
迭代器种类:常用的是双向迭代器和随机访问迭代器
2 容器算法迭代器初识
STL最常用的容器为Vector,可以理解为数组;
2.1 Vector存放内置数据类型
容器:vector;算法:for_each;迭代器:vector<int>::iterator
//-----------练习1:Vector遍历数据--------------------
void MyPrint(int val)
{
cout<<val<<endl;
}
void test01()
{
//创建vector容器对象,并且通过模板参数指定容器中存放数据的类型
vector<int> v;//像一个类模板,创建对象
//向容器中放数据
v.push_back(10);//成员函数,尾插法
v.push_back(20);
v.push_back(30);
v.push_back(40);
//每个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
//v.begin()返回迭代器,这个迭代器指向容器中的第一个数据
//v.end()返回迭代器,这个迭代器指向容器的最后一个元素的下一次位置
vector<int>::iterator pBegin=v.begin();
vector<int>::iterator pEnd=v.end();
//第一种遍历方式:
while(pBegin != pEnd)
{
cout<<*pBegin<<endl;
pBegin++;
}
//第二种遍历方式
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<endl;
}
//第三种遍历方式---使用STL提供标准遍历算法,头文件<algorithm>
for_each(v.begin(),v.end(),MyPrint);
}
int main()
{
test01();
return 0;
}
2.2 Vector存放自定义数据类型
//--------------练习2:Vector存放自定义数据类型------------------
class Person
{
public:
Person(string name,int age)
{
this->m_Name=name;
this->m_Age=age;
}
string m_Name;
int m_Age;
};
void test01()//存放Person
{
//创建vector容器
vector<Person> v;
//创建数据
Person P1("aaa",10);
Person P2("bbb",10);
Person P3("ccc",10);
//存放数据---尾插法
v.push_back(P1);
v.push_back(P2);
v.push_back(P3);
//遍历数据,将It看做指针
for(vector<Person>::iterator it=v.begin();it!=v.end();it++)
{
//cout<<"姓名:"<<(*it).m_Name<<" 年龄"<<(*it).m_Age<<endl;
cout<<"姓名:"<<it->m_Name<<" 年龄"<<it->m_Age<<endl;
}
}
void test02()//存放Person*
{
//创建vector容器
vector<Person*> v;
//创建数据
Person P1("aaa",10);
Person P2("bbb",10);
Person P3("ccc",10);
//存放数据---尾插法
v.push_back(&P1);
v.push_back(&P2);
v.push_back(&P3);
//遍历数据,将It看做指针
for(vector<Person*>::iterator it=v.begin();it!=v.end();it++)
{
cout<<"姓名:"<<(*it)->m_Name<<" 年龄"<<(*it)->m_Age<<endl;
}
}
int main()
{
test02();
return 0;
}
2.3 Vector容器嵌套容器,vector---一维数组
//-----------练习3:Vector容器嵌套容器,vector:一维数组--------
int main()
{
//创建大容器
vector<vector<int>> v;
//创建小容器
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
//向小容器中添加数据,尾插法
for(int i=0;i<5;i++)
{
v1.push_back(i+1);
v2.push_back(i+2);
v3.push_back(i+3);
v4.push_back(i+4);
}
//向大容器中插入小容器
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//遍历数据
for(vector<vector<int>>::iterator it=v.begin();it!=v.end();it++)
{
//(*it)---是vector<int>
for(vector<int>::iterator vit=(*it).begin();vit!=(*it).end();vit++)
{
cout<<(*vit)<<" ";
}
cout<<endl;
}
return 0;
}
3 string容器---本质为类
本质:string是C++风格的字符串,而string本质是一个类
string和char*区别:(1)char*是一个指针;
(2)string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器;
特点:string类内部封装了很多成员函数,如find,copy,delete,replace,insert,
string管理char*分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
3.1 string构造函数
(1)string();//创建一个空的字符串,例如:string str;
(2)string(const char* s);//使用字符串S初始化
(3)string(const string& str);//使用一个string对象初始化另一个string对象
(4)string(int n,char c);//使用n个字符c初始化
void test01()
{
string s1;//(1)默认构造,空
const char* str="hello world";
string s2(str);//(2)字符串初始化
string s3(s2);//(3)拷贝
string s4(10,'a');//(4)n个字符初始化
}
3.2 string赋值操作
//------------练习4:string字符串赋值----------------
void test()
{
string str1="hello world";//(1)char*类型字符串=赋值
cout<<str1<<endl;//hello world
string str2=str1;//(2)字符串=赋值
cout<<str2<<endl;//hello world
string str3;
str3='a';//(3)字符=赋值,重载=运算符
cout<<str3<<endl;//a
string str4;
str4.assign("hello C++");//(4)assign字符串赋值
cout<<str4<<endl;//hello C++
string str5;
str5.assign("hello C++",5);//(5)assign字符串前N个赋值
cout<<str5<<endl;//hello
string str6;
str6.assign(str5);//(6)assign拷贝
cout<<str6<<endl;//hello
string str7;
str7.assign(10,'b');//(7)assign中n个字符c赋值
cout<<str7<<endl;//bbbbbbbbbb
}
int main()
{
test();
return 0;
}
3.3 string字符串拼接---实现字符串末尾拼接字符串
str2.append(str2,4,3);//str2="LOL DNF";就将从第4个字符开始,截取3个字符,进行拼接
3.4 字符串的查找替换
查找:查找指定字符串是否存在;替换:在指定位置替换字符串
总结:(1)返回-1就是找不到,否则返回位置坐标(默认int pos=0)
(2)rifnd是从右往左查找,find从左往右查找,都是查找返回第一次出现的位置
string str1="abcdefg";
//从1号位置起,3个字符替换为“111111”
str1.replace(1,3,"111111");
cout<<"str1="<<str1<<endl;//输出为“a111111efg”
3.5 字符串比较
字符串比较是按照非负的ASCII码进行对比,=0,str1>str2返回1,str1<str2返回-1
string str1="hello";
string str2="hello";
if(str1.compare(str2)==0){
cout<<"str1等于str2"<<endl;
}
总结:字符串对比主要是比较两个字符串是否相等,判断大小的意义并不大
3.6 string字符存取
//----------练习5:string字符存取----------------
void test()
{
string str1="hello";
//------------读取单个字符--------------
//1、通过重载[]访问单个字符
for(int i=0;i < str1.size();i++)//size()成员函数,计算字符串长度
{
cout<<str1[i]<<endl;
}
//2、通过at成员函数来访问单个字符
for(int i=0;i < str1.size();i++)
{
cout<<str1.at(i)<<endl;
}
//---------------修改单个字符------------
str1[0]='a';
str1.at(1)='b';
cout<<str1<<endl;//输出为abllo
}
int main()
{
test();
return 0;
}
3.7 string插入和删除
string str="hello";
str.insert(1,"111");//插入结果为:h111ello
str.erase(1,3);//删除,从第1个位置处删除3个字符,结果为hello
总结:插入和删除的起始位置下标都是从0开始;
3.8 string子串---从字符串中获取想要的子串
//------------练习6:string字符串截取子串--------
void test01()
{
string str="abcdef";
string subStr=str.substr(1,3);
cout<<"subStr="<<subStr<<endl;
}
//实用操作,获取邮箱用户名
void test02()
{
string email="Tom@sina.com";
int pos=email.find('@');//pos=3
string usrName=email.substr(0,pos);
cout<<usrName<<endl;
}
int main()
{
test02();
return 0;
}
4 Vector容器---动态扩展的数组
4.1 vector基本概念
功能:vector数据结构和数组非常相似,也称为单端数组;
vecotor与普通数组区别:不同之处在于数组是静态空间,而vector可以动态扩展;
动态扩展:
并不是在原空间之后续接新空间,而找更大的内存空间,然后将原数据拷贝新空间,释放原空间;
vector容器的迭代器是支持随机访问的迭代器;
4.2 vector构造函数---创建vector容器
//-----------练习7:vector构造函数------------
void MyPrint(vector<int>& v)
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<" ";}
cout<<endl;
}
void test()
{
vector<int>v1;//默认构造,无参构造
for(int i=0;i<10;i++)
{
v1.push_back(i);
}
MyPrint(v1);//输出为0 1 2 3 4 5 6 7 8 9
vector<int>v2(v1.begin(),v1.end());//通过区间方式拷贝构造
MyPrint(v2);//输出为0 1 2 3 4 5 6 7 8 9
vector<int>v3(10,20);//n个元素构造
MyPrint(v3);//输出为20 20 20 20 20 20 20 20 20 20
vector<int>v4(v3);//拷贝构造函数
MyPrint(v4);//输出为20 20 20 20 20 20 20 20 20 20
}
int main()
{
test();
return 0;
}
4.3 vector容器-赋值操作
总结:(1)assign中,区间为前闭后开;(2)注意构造和赋值,赋值是构造之后,进行赋值
4.4 vector容器-容量和大小
//------------练习8:vector的容量和大小-----------
void MyPrint(vector<int>&v)
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
cout<<*it<<" ";
cout<<endl;
}
void test()
{
vector<int>v1;
for(int i=0;i<10;i++)
{
v1.push_back(i);//尾插法填充数据
}
if(v1.empty()){//返回1为空
cout<<"空"<<endl;}
else{
cout<<"非空"<<endl;}
cout<<"vector容量:"<<v1.capacity()<<endl;//13---动态拓展
cout<<"vector大小:"<<v1.size()<<endl;//10
MyPrint(v1);//0 1 2 3 4 5 6 7 8 9
v1.resize(15);//默认填充0
MyPrint(v1);//0 1 2 3 4 5 6 7 8 9 0 0 0 0 0
v1.resize(20,8);//指定填充
MyPrint(v1);//0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 8 8 8 8 8
}
int main()
{
test();
return 0;
}
4.5 vector容器-插入和删除
//------------练习9:vector的插入和删除----------
void MyPrint(vector<int>& v)
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
cout<<*it<<" ";
cout<<endl;
}
void test()
{
vector<int> v1;
for(int i=0;i<5;i++)
{
v1.push_back(i);//尾插法填充数据
}
MyPrint(v1);//0 1 2 3 4
v1.pop_back();//删除最后一个元素
MyPrint(v1);//0 1 2 3
v1.insert(v1.begin(),10);//插入一个数据
MyPrint(v1);//10 0 1 2 3
v1.insert(v1.begin(),2,100);//输入n个元素
MyPrint(v1);//100 100 10 0 1 2 3
v1.erase(v1.begin());//删除迭代器指向的元素
MyPrint(v1);//100 10 0 1 2 3
v1.erase(v1.begin(),v1.end());//删除迭代器区间内的元素
MyPrint(v1);
v1.clear();//删除容器中的所有元素
MyPrint(v1);
}
int main()
{
test();
return 0;
}
4.6 vector容器-数据存取
总结:可以[]方式访问数组中元素
4.7 vector容器-互换容器---实现两个容器内元素进行互换
//利用swap收缩空间,v1容量10万,占3
vector<int>(v1).swap(v1);//vector<int>(v1)利用拷贝创建匿名对象,swap();交换
总结:swap可以使两个容器互换,可以达到实用的收缩内存效果;
4.8 vector容器-预留空间---减少vector在动态扩展容量时的扩展次数
//------------练习10:vector容器-预留空间-------
void test()
{
vector<int>v1;
//利用reserve预留空间
v1.reserve(10000);
int num=0;//统计动态扩展开辟内存次数
int* p=NULL;
for(int i=0;i<10000;i++)
{
v1.push_back(i);
if(p!=&v1[0])
{
p=&v1[0];
num++;
}
}
cout<<"num="<<num<<endl;//24次放10000个,reverse之后,num=1
}
int main()
{
test();
return 0;
}
总结:如果数据量级很大,可以提前预留空间,减少动态扩展的次数