文章目录
1. stl基本概念
Standard Template Library,由惠普开发的一系列软件的统称,现在主要用在c++中,内嵌在编译器中,不需要安装。
熟练使用stl才算优秀的c++程序员。
stl从广义上分为3类:
- algorithm
- container,容器
- iterator,迭代器
容器和算法通过迭代器进行无缝连接,比传统的函数和类组成的库提供了更好的代码重用机会。
在c++标准中,stl有以下主要的头文件:
<algorithm>,<numeric>,<functional>
<iterator>
<vector>,<stack>,<queue>,<dequeue>,<list>,<map>,<set>
graph LR
A[STL]-->B[string]
A-->C[iterator]
C-->D[algorithm]
C-->E[container]
stl的6大组件
- Container,各种基本数据结构
- Algorithm
- Iterator,连接containers和algorithms
- Adapter,适配器),可改变containers、Iterators或Function object接口的一种组件
- Function object,函数对象
- Allocator,分配器
stl的重要特点是数据结构和算法的分离。
1.1 容器
重复利用已有的实现构造自己的特定类型下的数据结构。
数据结构:研究结点之间的关系。
容器分为2类:
- 序列式容器,sequence containers,每个元素有固定位置,与添加时机和地点有关,与值无关,如
vector,deque,list
。 - 关联式容器,associated containers,和添加顺序无关,节点组成红黑树,如
set,multiset,map,multimap
。
1.2 c++标准库
分为10类。
语言支持相关:<cstddef>,<limits>,<climits>,<cfloat>,<cstdlib>,<new>,<typeinfo>,<exception>,<cstdarg>,<csetjmp>,<csignal>
.
io流相关:<iostream>,<iomanip>,<ios>,<istream>,<ostream>,<sstream>,<fstream>,<iosfwd>,<streambuf>,<cstdio>,<cwchar>
诊断功能:<stdexcept>,<cassert>,<cerrno>
工具:<utility>,<functional>,<memory>,<ctime>
字符串处理:<string>,<cctype>,<cwctype>,<cstring>,<cwchar>,<cstdlib>
容器类的模板:<vector>,<list>,<deque>,<queue>,<stack>,<map>,<set>,<bitset>
迭代器:<iterator>
算法:<algorithm>,<cstdlib>,<ciso646>
数值操作:<complex>,<valarray>,<numeric>,<cmath>,<cstdlib>
本地化:<locale>,<clocale>
1.3 值拷贝
所有容器提供值语义value
,而不是引用语义reference
。所以执行添加操作时,要确保容器内的元素有拷贝构造函数,无参构造函数并重载赋值运算符。尤其是有指针成员时。
2. string
重点有初始化、遍历、连接、查找和替换、插入、反转和大小写转换。
而且,不用担心越界的问题。
2.1 初始化
//初始化
#include <iostream>
using namespace std;
#include "string"
int main()
{
string s1 = "aaa";
string s2("bbb");
string s3 = s1; // copy constructor
string s4(3,'c');
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
cout<<s4<<endl;
printf("%p\n%p\n%p\n","aaa",&s1,&s3);
cin.get();
return 0;
}
/*
aaa
bbb
aaa
ccc
00445000
0066FF00
0066FEE0
*/
2.2 遍历
3种方法:数组,迭代器,at()
。
#include <iostream>
using namespace std;
#include "string"
int main()
{
string s = "abcdef";
for(int i = 0; i < s.length(); i++)
{
cout<<s[i]<<" ";
}
cout<<endl;
for(string::iterator it = s.begin(); it != s.end(); it++)
{
cout<<*it<<" ";
}
cout<<endl;
for(int i = 0; i < s.length(); i++)
{
cout<<s.at(i)<<" ";
}
cout<<endl;
cin.get();
return 0;
}
/*
a b c d e f
a b c d e f
a b c d e f
*/
调试一下可以验证,s.end()
指向s[s.size()]
,也就是最后一个元素之后。
相比于[]
,at()
可以抛出异常。
#include <iostream>
using namespace std;
#include "string"
int main()
{
string s = "abcdef";
try
{
for(int i = 0; i < s.length() + 3; i++)
{
cout<<s.at(i)<<" ";
}
cout<<endl;
}
catch(...)
{
cout<<"Exception.\n";
}
try
{
for(int i = 0; i < s.length() + 3; i++)
{
cout<<s[i]<<" ";
}
cout<<endl;
}
catch(...)
{
cout<<"Exception.\n";
}
cin.get();
return 0;
}
//vs中第二次遍历会中断
2.3 转换为指针
初始化时可以实现char*-->string
,通过const char* c_str() cosnt{}
,就能实现string-->char*
。
把string
拷贝到一段内存中,可以用int copy(char *s,int n,int pos=0) const;
,返回值为拷贝数,该函数不保证不越界。
#include<iostream>
using namespace std;
#include "string"
int main(int argc, char *argv[])
{
string s = "abcdef";
//string-->char*
printf("%s\n",s.c_str());
//string-->buf
char buf[10] = {0};
s.copy(buf,5);
printf("%s\n",buf);
cin.get();
return 0;
}
/*
abcdef
abcde
*/
2.4 连接
可以用重载的加号运算符或者string &append();
,第2个参数如下:
const char*
const char *s,int n
const string &s
const string &s,int pos,int n
int n,char c
,添加n个c
#include<iostream>
using namespace std;
#include "string"
int main(int argc, char *argv[])
{
string s1 = "abc";
string s2 = "def";
s1 = s1 + s2;
cout<<s1<<endl;
string s3 = "23";
string s4 = "33";
s3.append(s4);
cout<<s3<<endl;
cin.get();
return 0;
}
2.5 查找和替换
string substr(int pos = 0,int n=npos) const;
返回由pos开始的长度n的字符串。
int find(char c, int pos=0) const;
int find(const char*s,int pos=0) const;
int find(const string &s,int pos=0) const;
这3个find()
也有对应的rfind()
,从后往前查找,pos默认参数为string::npos
。
string:npos是个特殊值,说明查找没有匹配
string &replace(int pos,int n,const char*s)
,删除pos开始的n个字符,插入s。
void swap(string &s)
,交换当前字符串与s的值。
#include<iostream>
using namespace std;
#include "string"
int main(int argc, char *argv[])
{
string s = "aaa When I aaa was aaa.";
int index = 0;
index = s.find("When");
cout<<index<<endl;
index = s.find("When",index+1);
cout<<index<<endl;
index = s.find("aaa");
int flag = 0;
while(index != string::npos)
{
flag++;
s.replace(index,3,"AAA");
index = s.find("aaa",index+1);
}
cout<<flag<<endl;
cout<<s<<endl;
cin.get();
return 0;
}
/*
4
-1
3
AAA When I AAA was AAA.
*/
2.6 删除和插入
string &erase(int pos=0, int n=npos);
,删除pos处的n个字符。
iterator erase ( iterator first, iterator last );
string &insert(int pos,const char*s);
string &insert(int pos,const string &s);
string &insert(int pos, int n, int c);
#include<iostream>
using namespace std;
#include "string"
int main(int argc, char *argv[])
{
string s = "hello world.";
string::iterator it = find(s.begin(),s.end(),'l');
if(it != s.end())
{
s.erase(it);
}
cout<<"s: "<<s<<endl;
s.erase(s.begin(),s.end());
cout<<s<<endl;
cout<<s.length()<<endl;
s.insert(0,"new string.");
cout<<"s: "<<s<<endl;
cin.get();
return 0;
}
/*
s: helo world.
0
s: new string.
*/
2.7 算法
#include<iostream>
using namespace std;
#include "string"
#include "algorithm"
int main(int argc, char *argv[])
{
string s = "aaaBBB";
transform(s.begin(), s.end(), s.begin(), toupper);
cout << s << endl;
transform(s.begin(), s.end(), s.begin(), tolower);
cout << s << endl;
cin.get();
return 0;
}
3. vector容器
vector容器把元素存进动态数组,尾部添加或删除快,中间添加或删除需要移动后面的元素。vector可以随机存储元素,并支持[]
和at()
。
vector采用模板类实现,即vector<T> vecT;
存放自定义类时,由于需要值复制,所以必须提供拷贝构造函数。
3.1 构造、添加、删除
v.front()
和v.back()
返回第一个和最后一个元素的引用。
void push_back()
和void pop_back
是末尾插入和删除。
#include<iostream>
using namespace std;
#include "vector"
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
cout<<"len:"<<v1.size()<<endl;
cout<<"the first element:"<<v1.front()<<endl;
v1.front() = 11;
v1.back() = 33;
while(v1.size() > 0)
{
cout<<v1.back()<<endl;
v1.pop_back();
}
cin.get();
return 0;
}
/*
len:3
the first element:1
33
2
11
*/
可以指定元素个数并初始化为0:vector<int> v1(3)
#include<iostream>
using namespace std;
#include "vector"
int main(int argc, char *argv[])
{
vector<int> v1(3); // vector<int> v1(3,0)
v1.push_back(1);
cout<<v1.size()<<endl;
for(int i = 0; i < 4; i++)
{
cout<<v1[i];
cout<< " ";
cout<<v1.at(i)<<endl;
}
cin.get();
return 0;
}
/*
4
0 0
0 0
0 0
1 1
*/
除了vector<int> v1
,还有其它带参数构造方式:
vector(iterator begin,iterator end)
,拷贝左闭右开区间[begin,end)
的元素vector(n,elem)
,拷贝n个elemvector(const vector &vec)
,拷贝构造函数
vector<int> v2 = v1;
vector<int> v3(v1.begin(),v2.end() + 2);
3.2 迭代遍历
#include<iostream>
using namespace std;
#include "vector"
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout<<*it<<" ";
}
cout<<endl;
for(int i = 0; i < 3; i++)
{
printf("%p\n",&v1[i]);
}
printf("%p\n",&*v1.end());
cin.get();
return 0;
}
/*
1 2 3
006C2F88
006C2F8C
006C2F90
006C2F94
*/
v1.end()
仍然指向最后一个元素+1
.
反过来,还有rbegin(),rend()
#include<iostream>
using namespace std;
#include "vector"
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for(vector<int>::reverse_iterator rit = v1.rbegin(); rit != v1.rend(); rit++)
{
cout<<*rit<<" ";
}
cout<<endl;
cin.get();
return 0;
}
/*
3 2 1
*/
迭代器
迭代器分为:
- 输入迭代器,也叫只读迭代器,读取一个元素后移动,只能遍历一遍
- 输出迭代器,也叫只写迭代器
- 正向迭代器,相当于输入+输出迭代器,并能多次遍历
- 双向迭代器,相当于正向迭代器+移动操作符
- 随机访问迭代器,可以直接访问任何位置的双向迭代器
双向迭代器支持的操作:
it++,++it,
*it,
it1 = it2
it1 == it2, it1 != it2
支持的容器有list,set,multiset,map,multimap
随机访问迭代器在双向迭代器的基础上添加了以下操作:
it+=i,it[i]
it1<it2,it1<=it2
it1 = it2
it1 == it2, it1 != it2
支持的容器有vector,deque
.
3.3 插入
vector.insert(iterator pos,elem);
vector.insert(iterator pos,int n,elem);
vector.insert(iterator pos,begin,end);
,在pos插入[begin,end)
区间的值。
3.4 删除
vector.erase(iterator pos)
vector.erase(iterator begin,iterator end)
,删除一个区间。
注意,发生插入和删除时,vector的元素会移动,但迭代器不会改变所指向的位置。
#include<iostream>
using namespace std;
#include "vector"
int main(int argc, char *argv[])
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(2);
v1.push_back(3);
vector<int>::iterator it = v1.begin();
v1.erase(it);
v1.erase(it);
for(int i = 0; i < v1.size(); i++)
{
cout<<v1[i]<<" ";
}
cout<<endl;
v1.insert(v1.begin(),2);
v1.insert(v1.begin(),1);
cout<<"after insert(),it still point to begin:";
cout<<*it<<endl;
cout<<"deleting..."<<endl;
for(; it != v1.end();) //delete 2
{
if(*it == 2)
{
v1.erase(it);
}
else
{
it++;
}
}
for(int i = 0; i < v1.size(); i++)
{
cout<<v1[i]<<endl;
}
cin.get();
return 0;
}
/*
2 3
after insert(),it still point to begin:1
deleting...
1
3
*/