1.概述
#include <iterator> //迭代器,包含c++11的begin() 和end()函数
#include <array> //c++11 数组类型,长度固定,提供了更好、更安全的接口,执行效率和内置数组相同,可以有效替代内置数组
#include <valarray> //c++11 值类型的数组类型,针对值类型的数组,有更多的操作,比如求和,最大最小数等。
#include <list> //双向链表,插入删除速度快,不支持随机访问
#include <forward_list> //c++11 单向链表,单向访问,插入删除速度快,不支持随机访问,没有size操作
#include <deque> //双端队列,支持快速随机访问
#include <string> //string类型,插入删除耗时
#include <vector> //迭代器类型,插入删除耗时
#include <iostream>
/*
* 具体需要哪个容器根据它的数据结构的优势来选择
* C++11 vector<vector<int>> “>>”不用添加空格了
*/
2、迭代器
如果begin和end相等,则范围为空
如果begin和end不等,则范围至少包括一个元素,且begin指向范围中的第一个元素
while (begin ! = end) {
*begin = val;//正确,范围非空
++begin;
}
迭代器不能比较大小
list<int>il = {1,2,3,4,5,6,7};
list<int>::iterator it = il.begin(), it2 = il.end();
while(it < it2) //error
{ ...}
容器类形成员
size_type、iterator、const_iterator
//iil是通过list<int>定义的一个迭代器类型
list<int>::iterator iil = il.begin();
c++11新增加了auto和begin,end的结合用法。
增加了cbegin和crbegin。
#include <iterator>
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<string>il = {"hello", "world", "wang", "wei", "hao" };
auto it1 = il.begin(); //list<string>::iterator
auto it2 = il.cbegin(); //list<string>::const_iterator
auto it3 = il.rbegin(); //list<string>::reverse_iterator
auto it4 = il.crbegin(); //list<string>::const_reverse_iteratror
cout << *it1 << endl;
//*it2 = "ww"; error:const类型不能修改
cout << *it2 << endl;
cout << *it3 << endl;
cout << *it4 << endl;
//*it4 = "ww"; error:const类型不能修改
}
容器定义和初始化
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int>ivec1; //默认初始化
vector<int>ivec2 = {1,2,3,4,5,6,7,8,9,0}; //列表初始化
//vector<int>ivec2{1,2,3,4,5,6,7,8,9,0};
vector<int>ivec3(ivec2); //拷贝初始化
//vector<int>ivec3 = ivec2;
vector<int>ivec4(ivec1.begin(), ivec1.end());//迭代器初始化
vector<int>ivec5(10); //n个元素初始化
vector<int>ivec6(10, 9); //n个元素加初始值初始化为9
return 0;
}
为了创建一个容器为另一个容器的拷贝,两个容器的类型及其元素类型必须匹配
不过当传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的。而且新容器
和原容器中的元素类型也可以不同,!只要能将拷贝的元素转换即可
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <deque>
using namespace std;
int main()
{
list<string>il1 = {"hello", "world", "hehe"};//新标准中可以对一个容器进行列表初始化
list<string>il2(il1);
for(const string&s : il2)
cout << s << endl;
//deque<string>deq(il1);
deque<string>deq(il1.begin(), il1.end());
for(const string&s : deq)
cout << s << endl;
}
.标准库array具有固定大小
array类型是c++11数组类型,长度固定(必须初始的时候指定),提供了更好,更安全的接口,
执行效率和内置数组相同,可以有效替代内置数组
标准库array的大小也是类型的一部分,当定义一个array时,除了指定元素类型,还要指定容器大小
#include <array>
array<int, 10> a = {1,2,3,4,5,6,7,8,9,0};
array<string, 10> //保存10个string的数组
array<int, 10> b = a;
赋值和swap
如果两个容器原来大小不同,赋值运算后两者的大小与右边容器的原大小相同
与内置数组不同,标准库array类型允许赋值,赋值号左右两边的运算对象必须具有相同的类型
使用assign(仅顺序容器)
允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int>ivec;
ivec = {1,2,3,4,5,6,7,8};
for(const int &i: ivec)
cout << i << endl;
vector<int>ivec2 = {9,9,9};
ivec = ivec2; //直接赋值
for(const int &i: ivec)
cout << i << endl;
vector<int>ivec3 = {1,1,1,1,1};
vector<int>ivec4 = {2,2,2,2,2};
//swap(ivec3,ivec4); //swap的两种方式,统一使用非成语版本swap是一个好习惯
//ivec3.swap(ivec4);
for(const int &i:ivec3)
cout << i << endl;
for(const int &i:ivec4)
cout << i << endl;
ivec3.assign(10,11); //赋值assign的几种方式
//ivec3.assign({2,2,2,2,2,2});
//ivec3.assign(ivec4.begin(), ivec4.end());
for(const int &i:ivec3)
cout << i << endl;
vector<string> svec1(10);
vector<string> svec2(24);
swap(svec1,svec2);
}
交换两个容器保证会很快,元素本身并为交换,swap只是交换了两个容器的内部结构。
意味着指向容器的迭代器,引用,指针在swap操作之后都不会失效
容器大小操作
size( ):返回容器中元素的个数
empty( ):查看容器是否为空,size=0时返回true
max_size( ):返回一个大于或等于该容器所能容纳最大元素数的值
forward_list不支持size( )操作。
关系运算符
每个容器都支持(==和!=),除了无序关联容器所有容器都支持(>、<、>=、<=)
关系运算符左右两端必须容器类型相同。
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器。
3、顺序容器操作添加元素
#include <iostream>
#include <string>
#include <list>
#include <vector>
using namespace std;
class character
{
public:
character() =default;
character(string name, double power):userName(name), userPower(power), sex(0) {}
character(string name, bool s, double power, int age) :userName(name), sex(s), userPower(power), userAge(age) {}
string userName = "";
double userPower = 0.0;
bool sex = 0;
int userAge = 0;
};
int main() {
vector<character> ivec;
character p1("diyupaoxiao",1,100,20);
ivec.push_back(p1); //c.push_back()尾部"创建"一个元素,返回void,注意创建这个词,说明它会重新建立一个元素,而不是以前的
ivec.emplace_back("diyupaoxiao", 1, 100, 20); //C++11 c.emplace_back()同上,区别是传递的是参数,emplace传递的是参数,不是对象,就如左边
list<character> il;
il.push_front(p1); //c.push_front()头部创建一个元素,返回void
il.emplace_front("diyupaoxiao", 1, 100, 20);
il.emplace_front("paogeer", 100); //因为emplace会用参数通过容器构造对象,所以只传递了171参数,构造时调用people类的第二个构造函数
auto iter = il.begin();
iter = il.insert(iter,p1);//inser第一个参数是迭代器,指出在什么位置存放p1
il.emplace(iter, "diyupaoxiao", 1, 100, 20);
for (character &p : ivec)
{
cout << "name:" << p.userName << " sex:" << p.sex << " height:" << p.userPower << " age:" << p.userAge << endl;
}
cout << endl;
for (character &p : il)
{
cout << "name:" << p.userName << " sex:" << p.sex << " height:" << p.userPower << " age:" << p.userAge << endl;
}
vector<int> ivec2 = { 1 };
auto iter2 = ivec2.begin();
ivec2.insert(iter2, 3, 10); //c.insert(p, n, t) 迭代器p位置插入n个t元素
vector<int> ivec3 = { 1,2,3,4,5 };
auto iter3 = ivec3.begin();
ivec3.insert(iter3, ivec2.begin(), ivec2.end()); //c.insert(p, b, e) 迭代器p位置插入另一个类型相同容器迭代器(b,e)范围内的元素
vector<int>ivec4;
//ivec4.insert(ivec.begin(), {1,1,1,1,1,1,1,1}); //c.insert(p, il)迭代器p位置插入il一个花括号包围的初始值列表。但运行会报错不知是编译器的问题还是
cout << "ivec2" << endl;
for (const int &i : ivec2)
cout << i << endl;
cout << "ivec3" << endl;
for (const int &i : ivec3)
cout << i << endl;
cout << "ivec4" << endl;
for (const int &i : ivec4)
cout << i << endl;
system("pause");
return 0;
}
Note:
注意:
1). 向一个vector,string,deque插入元素会使所有指向容器的迭代器,引用,指针失效
2).当我们用一个对象来初始化容器时,或将一个对象插入到容器时,实际上放的是一个拷贝,而不是对象本身。
3).每个insert都接受一个迭代器作为第一个参数。
4).insert函数将元素都插入到敌人代器所指定的位置之前
5)、.将元素插入到vector,deque,string中的任何位置都是合法的。然而这样做可能会很耗时
6)、.c++11新标准下,接受元素的个数或范围的insert版本返回指向第一个元素的迭代器。如果范围为空,不插入任何元素。
7).使用emplace:新标准引入了三个成员,emplace_front, emplace, emplace_back,这些操作构造而不是拷贝元素。
当我们调用emplace成员函数时,则是将参数传递给元素类型的构造函数。emplace成员使用这些参数在容器管理的内存空间中直接构造元素。
emplace_back会在容器管理的内存空间中直接创建对象,而调用push_back则会创建一个局部临时对象,并将其压入容器中。
emplace函数在容器中直接构造元素,传递给emplace函数的参数必须与元素类型的构造函数相匹配。
访问元素
包括array在内的每个顺序容器都有一个front函数成员,而除forward_list之外的所有顺序容器都有一个back成员函数。这两个操作分别返回
首元素和尾元素的引用。
当然可以用迭代器c.begin( )和(c.end( ))--。但是都要确保容器非空,如果容器为空,行为是未定义的
if (!c.empty()) {
//val1和val2是c中第一个元素的拷贝
auto val1 = *c.bngin(), val2 = c.end();
//val3和val4是c中最后一个元素的拷贝
auto last = c.end();
auto val3 = *(--last);//不能递减forward_list
auto val4 = c.back();//forward_list不支持
}
顺序容器访问元素的操作例子
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int>ivec = {1,2,3,4,5,6,7,8,9,0};
//vector<int>ivec;
cout << ivec[0] << endl; //c[n] 返回下标为n的元素的引用 n>=c.size() 结果是未定义的
cout << ivec.at(0) << endl; //c.at(n) 返回下标为n的元素的引用,如果下标越界,则抛出异常out_of_range
cout << ivec.front() << endl; //c.front() 返回容器的第一个元素
cout << ivec.back() << endl; //c.back() 返回容器的最后一个元素
cout << *(ivec.begin()) << endl;
}
注意:
1)、.迭代器c.end( )是末尾元素的下一个位置
2)、.为确保下标是合法的,我们可以使用at
删除元素
#include <vector>
#include <list>
#include <iostream>
using namespace std;
int main()
{
int ia[] = {1,2,3,4,5,6,7,8,9,0};
vector<int>ivec;
list<int>il;
for(int i = 0; i < 10; ++i)
{
ivec.push_back(ia[i]);
il.push_back(ia[i]);
}
for(auto it = ivec.begin(); it != ivec.end();)
{
if(*it%2 == 0)
{
it = ivec.erase(it); //!坑,erase返删除元素的下一个迭代器。
}
else
it++;
}
for(auto it = il.begin(); it != il.end();)
{
if(*it%2 == 1)
{
it = il.erase(it);
}
else
it++;
}
for(const int &i : ivec)
cout << i << endl;
for(const int &i : il)
cout << i << endl;
}
//c.pop_back() 删除尾元素,返回void
//c.pop_front()删除首元素,返回void
//c.erase(p) 删除迭代器p指向的元素,返回被删除元素的下一个元素的迭代器
//c.erase(b,e) 删除迭代器(b,e)范围内的元素,返回e的下一个元素的迭代器
//c.clear() 删除c中所有的元素
注意:
1)、.删除操作会改变容器的大小,所以不适合array
2)、.forward_list不支持pop_back, vector和string不支持pop_front
3)、.删除deque中除首尾位置之外的任何元素都会使所有迭代器,引用指针失效
4)、.删除前必须保证他们是存在的
5)、.注意删除返回迭代器的那些操作在循环中需要做出哪些改变!
特殊的forward_list操作
forward_list其实就是数据结构的单向链表
#include <forward_list>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
forward_list<int>ft = {1,2,3,4,5,6,7,8,9,0};
vector<int>ivec = {11,22,33,44};
for(const int&i : ft)
{
cout << i << " ";
}
cout << endl;
auto iter1 = ft.before_begin(); //一般返回的否是操作对象先前的迭代器
auto iter2 = ft.begin();
//auto iter = ft.cbefore_begin(); 返回一个const_iterator
cout << *iter1 << endl; //返回单向链表首元素的前一个位置迭代器,解引用未定义,随机值
cout << *iter2 << endl; //首元素
//insert
//ft.insert_after(iter2, 100); //插入到迭代器iter2指向的元素后面
//ft.insert_after(iter2, 10, 100); //10个100
//ft.insert_after(iter2, ivec.begin(), ivec.end());//插入范围,b,e不能使本对象中的范围
//ft.insert_after(iter2, {11,22,33,44,55});
for(const int&i : ft)
{
cout << i << " ";
}
cout << endl;
ft.emplace_after(iter2, 111);
for(const int&i : ft)
{
cout << i << " ";
}
cout << endl;
//erase //返回的是擦出元素的下一个元素,一定要注意
//ft.erase_after(iter2); //擦除的是迭代器所指向的后一个元素
ft.erase_after(iter1, ft.end()); //擦除的是迭代器所指向的元素的后一个元素开始
for(const int&i : ft)
{
cout << i << " ";
}
cout << endl;
return 0;
}
Note:
1)、.一般要处理整个容器时,要保存两个迭代器。curr用来找元素,prev用来删元素
auto curr = ft.begin( );
auto prev = ft.before_begin( );
2).使用erase_after后一定要记得修改curr
curr = ft.erase_after(n);//删除p指向位置后的元素,返回被删元素之后的元素迭代器
改变容器大小
c.resize(n) :调整c大小为n个元素。若n<c.size( ),则多出的元素被丢弃,若必须添加新元素,新元素使用值初始化
c.resize(n,t) :调整c大小为n个元素。任何新添加的元素都初始值为t。
!如果缩小容器,则指向被删除元素的迭代器,引用和指针都会失效。对vector,string,deque,resize可能导致迭代器,指针引用失效。
!如果容器保存的是类类型的元素,向容器添加元素时我们必须提供初始值,或元素必须提供默认构造函数。
容器操作可能使迭代器失效
是否会使迭代器失效还要看迭代器具体使用什么数据结构实现的。
比如forward_list这个容器就比较特殊,insert( )返回的是插入元素位置的迭代器。删除erase( p )删除的是p的下一个位置的元素,返回的是删除元素下一个位置的迭代器。
因为它是由单向链表实现,指针是单向的,我们不能返回去访问先前的元素,那么删除必须指定的元素必须要保存prev先前的迭代器,不然无法实现删除。
所以每个容器具体实现的数据结构是重点。
一个例子
复制容器中元素值是奇数的,删除容器中的元素值是偶数的。
好好体会一下,这些容器的差别
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
using namespace std;
int main()
{
//vector
vector<int>ivec = {1,2,3,4,5,6,7,8,9,0};
auto iter = ivec.begin();
while(iter != ivec.end())
{
if(*iter %2 == 1)
{
iter = ivec.insert(iter, *iter);
iter += 2;
}
else
iter = ivec.erase(iter);
}
for(const int&i : ivec)
cout << i << " ";
cout << endl;
//list
list<int>il = {1,2,3,4,5,6,7,8,9,0};
auto iter2 = il.begin();
while(iter2 != il.end())
{
if(*iter2%2 == 1)
{
il.insert(iter2, *iter2);
iter2++;
}
else
{
iter2 = il.erase(iter2);
}
}
for(const int&i : il)
cout << i << " ";
cout << endl;
//forward_list
forward_list<int>ft = {1,2,3,4,5,6,7,8,9,0};
auto fter1 = ft.before_begin();
auto fter2 = ft.begin();
while(fter2 != ft.end())
{
if(*fter2%2 == 1)
{
fter1 = fter2;
fter2 = ft.insert_after(fter2, *fter2);
fter1++;
fter2++;
}
else
{
fter2 = ft.erase_after(fter1);
}
}
for(const int&i : ft)
cout << i << " ";
cout << endl;
}
给容器中每个元素后面插入值#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int>ivec = {1,2,3,4,5,6,7,8,9,0};
auto iter = ivec.begin();
while(iter != ivec.end())
{
iter++; //前插
iter = ivec.insert(iter, 1);//插入后迭代器失效,如果不赋值给iter,iter失效。
iter++;
}
for(const int&i : ivec)
cout << i << " ";
cout << endl;
}
如果把iter = ivec.insert(iter, 1)写成ivec.insert(iter, 1)程序会出很大的错误。
迭代器失效了我们却没有保存iter。
!注意:
1)、对于insert来说,除了forward_list插入是后插外,其他容器是前插,前插返回前插的元素的迭代器,记得保存,插入后迭代器会失效。后插返回的是后插元素的迭代器。
总之不管插入位置,只要插入返回的就是插入的元素的迭代器,但是要考虑它的位置是前插还是后插。
2)、管理迭代器,确认每次改变容器的操作之后都正确的重新定位迭代器。这个对vector,string,deque尤其重要。
3)、记不起来时想下它的数据结构。
4、vector对象如何增长
标准库实现者采用了可以减少容器控件重新分配次数的策略,当不得不获取新内容空间时,vector和string通常会分配比新的需求更大的内存空间,以作备用。
#include<iostream>
#include<vector>
using namespace vector;
using namespace std;
int main(){
vector<int> ivec;
//size应该为0,capacity依赖于具体实现
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
//向ivec添加24个元素
for(vector<int>::size_type is = 0;ix!=24;++ix){
ivec.push_back(ix);
}
//size应该为24,capacity应该大于等于24
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
ivec.reserve(50);//将capacity至少设定为50,可能会更大
//size:24,capacity>=50
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
//添加元素用光多余容量
while(ivec.size()!=ivec.capacity()){
ivec.push_back(10);
}
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
//再添加一个新元素,
ivec.push_back(1001);
//capacity:100
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
ivec.shrink_to_fit();//要求归还内存
cout<<"ivec size:"<<ivec.size()<<"capacity:"<<ivec.capacity()<<endl;
return 0;
}
5、额外的string操作构造string的其他方法
#include<iostream>
#include<vector>
using namespace vector;
using namespace std;
int main(){
//当我们从一个const char*创建一个string时,指针指向的数组必须以空字符结尾,拷贝操作遇到空字符时停止,如果传递给构造函数一个计数值,则不必
//当从一个string拷贝字符时,可以提供一个可选的开始位置和一个计数值
const char *cp ="hello HIT!!!";
char noNull[]={'H','I','T'};
string s1(cp);//从=拷贝cp中的字符直到遇到空字符
string s2(noNull,2);//从nonull中拷贝两个字符
string s3(noNull);//未定义,noNull不是空字符结束
string s4(cp+6,3);//从cp[6]开始拷贝3个字符
string s5(s1,6,3);//从s1[6]开始拷贝3个字符
string s6(s1,6); //从s1[6]开始拷贝,直至末尾
string s7(s1,6,20);//正确,之拷贝到s1末尾
string s8(s1,12);//抛出异常
string s("hello world!!!");
string s9=s.substr(0,5);//s9=hello
string s10=s.substr(6);//s10=world
string s11=s.substr(6,11)//s11=world
cout<<s1<<" "<<s2<<" "<<s3<<" "<<s4<<" "<<s5<<" "<<s6<<" "<<s7<<" "<<s8<<endl;
return 0;
}
改变string的其他方法
#include<iostream>
#include<vector>
using namespace vector;
using namespace std;
int main(){
string s = "hello,world!";
//string除了assign和insert,erase操作,还定义了自己的insert和erase版本
s.insert(s.size(), 5, '!'); //末尾插入5个感叹号
cout << s << endl;
s.erase(s.size()-5, 5); //删除最后的5个字符
cout << s << endl;
//标准库string还定义了接受c风格字符数组的insert和assign版本
string s1;
const char *p = "hello, world!";
//s.assign(args) 将s中的字符替换为args指定的字符
s1.assign(p, 7); //复制p的前7个字符
cout << s1 << endl;
//s1.insert(s.size(), p+4); //error: out_of_range
//cout << s1 << endl;
string s2 = "hello", s3 = "world";
s2.insert(0, s3); //将s3插入到s2的0起始位置
cout << s2 << endl;
s2.insert(0, s3, 0, s3.size()-1); //从s2 的0处插入s3的(0, s3.size()-1)范围的元素
cout << s2 << endl;
string s("c++ primer");
s.append(" 5th Ed."); //在末尾追加字符串
//s.insert(s.size(), " 5th Ed."); 和上面等价
cout << s << endl;
//replace操作是调用erase和insert的一种简写方式
//s.replace(11, 3, "4th");
s.erase(11, 3);
s.insert(11, "4th");
cout << s << endl;
return 0;
}
string搜索操作
每个搜索都返回一个string::size_type值,表示匹配发生位置的下标。如果搜索失败,则返回一个string::npos的static成员
string的搜索操作
s.find(args) 查找args第一次出现的位置
s.rfind(args) 查找args最后一次出现的位置
s.find_first_of(args) 在s中查找args任何一个字符第一次出现的位置
s.find_last_of(args) 在s中查找args任何一个字符最后一次出现的位置
s.find_first_not_of(args) 在s中查找第一个不在args中的字符
s.find_last_not_of(args) 在s中查找最后一个不再args中的字符
args的形式
c,pos pos位置开始查找字符c,pos默认为0
s2,pos pos位置开始查找字符串s2,pos默认为0
cp,pos pos位置开始查找指针cp指向的以空字符结尾的C风格字符串,pos默认为0
cp,pos,n pos位置开始查找指针cp指向的数组的前n个字符。pos和n无默认值
#include<iostream>
#include<vector>
using namespace vector;
using namespace std;
int main(){
//find,区分大小写
//搜索成功返回一个string::size_type值,若失败,返回一个string::npos的static成员(unsigned)
string name("hello HIT!!!");
auto pos1 = name.find("HIT"); //pos返回的是第一次出现的下标,若找不到则返回一个最大数
cout << pos1 << endl;
string s1("0123456789");
string s2("0rrr1r");
//查找给定字符串中任何一个字符匹配的位置。
auto pos2 = s2.find_first_of(s1); //s1在name中出现的第一个数字在name中的下标
cout << pos2 << endl;
//搜索第一个不在参数中的字符
auto pos3 = s1.find_first_not_of(s2); //返回r所在的下标1
cout << pos3 << endl;
string::size_type pos=0;
//每步循环查找name中下一个数
while ((pos=name.find_first_of(s1,pos))!=string::npos) {
cout<<"found number at index:"<<pos<<"element is "<<s2[pos]<<endl
++pos;
}
return 0;
}
逆向搜索:
一般的find都是从左到右,rfind提供了从右到左。
compare函数:
等于大于小于返回0,正数或者负数
compare的6个版本如下
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1 = "aaacaaaaa";
string s2 = "aaaaaaaab";
char *p = "aaaaaaaac";
cout << s1.compare(s2) << endl; //s1>s2
cout << s1.compare(5, 3, s2) << endl; //s1从第五个字符开始的3个字符小于s2
cout << s1.compare(0, 5, s2, 0, 5) << endl; //从s1的0开始的5个字符和s2从0开始的5个字符比较
cout << s1.compare(p) << endl; //比较p指向的地址开始的字符串
cout << s1.compare(5, 4, p) << endl; //比较s1从第5个开始的4个字符和p指向的地址的字符串
cout << s1.compare(5, 4, p, 4) << endl; //比较s1从第5个开始的4个字符和p指向的地址的4个字符
}
数值转换:
标准引入了多个函数,可以实现数值数据与标准库string之间的转换
#include<iostream>
#include<vector>
using namespace std;
int main(){
int i = 100;
string s;
s = to_string(i);//将整数i转换为字符表示形式
double d = stod(s);//将字符串s转换为浮点数
cout << s << endl;
std::size_t m = 2;
string s1 = "1000";
int a = stoi(s1); //stoi
long b = stol(s1);//stol
unsigned long c = stoul(s1);//stoul
long long l = stoll(s1);//stoll
unsigned long long e = stoull(s1);//stoull
cout << "int:" << a << "long:" << b << "unsigned long:" << c << "longlong:" << l << "unsigned longlong:" << e << endl;
string s2 = "1000.11";
float f = stof(s2);//stof
double g = stod(s2);//stod
long double h = stold(s2);//stold
cout << "float:" << f << "double:" << g << "long double:" << h << endl;
return 0;
}
结果为:
100
int:1000long:1000unsigned long:1000longlong:1000unsigned longlong:1000
float:1000.11double:1000.11long double:1000.11
6、容器适配器
container_type是实现适配器的底层容器类型
三种:stack, queue, priority_queue
本质上:适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样
一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型
理解:容器适配器是基于已有的容器做的功能上的一种改进。
定义一个适配器:
每个适配器都有两个默认构造函数:默认构造函数创建一个空对象,接受一个容器的构造函数拷贝该容器来初始化适配器。
deque<int> deq;
stack<int> stk(deq); //用一个deque类型的容器来初始化stack适配器
//我们可以创建一个适配器时将一个命名的顺序容器作为第二个类型参数,来重载默认容器
stack<string, vector<string>> str_stk;
stack<string, vector<string>> str_stk2(ivecs);
注意:
因为所有适配器都要求容器具有添加、删除以及访问尾元素的能力,所以array和forward_list不能构造适配器
1.stack
stack:一种元素先进后出的一种容器
stack可以使用除了array和forward_list类型的其他任何类型的容器来构造
#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <deque>
using namespace std;
int main()
{
vector<int>ivec = {1,2,3,4,5,6,7,8,9,0};
deque<int>deq = {1,2,3,4,5,6,7,8,9,0};
//stack<int>stk(ivec); error:因为stack本来是使用deque改进的一种适配器,
//如果要用vector构造,必须像下面明确指定stack<int, vector<int>>.
stack<int>stk(deq);
stack<int, vector<int>>stk2(ivec);//明确指出stack在vector上面实现。
int val;
while(!stk.empty())
{
cout << stk.top() << endl;
stk.pop();
}
while(!stk2.empty())
{
cout << stk2.top() << endl;
stk2.pop();
}
}
//stack默认是deque实现,也可以在list或vector实现
//s.pop() 删除栈顶元素,但不返回元素值
//s.push(item) 创建一个新元素压入栈顶
//s.emplace(args) 同前面所说args是参数,动态构造一个对象
//s.top() 返回栈顶元素,但不将元素出栈
2.queue
queue:一种元素先进先出的容器
queue可以由list和deque来构造,但是不能由vector来构造,默认是deque构造
#include <queue>
#include <vector>
#include <list>
#include <deque>
#include <iostream>
using namespace std;
int main()
{
list<int>il = {1,2,3,4,5,6,7,8,9,0};
deque<int>deq = {1,2,3,4,5,6,7,8,9,0};
vector<int>ivec = {1,2,3,4,5,6,7,8,9,0};
//queue<int>que(il); error:queue默认是deque构造的,只能由deque来初始化
queue<int>que(deq);//yes
//当然可以显示指定由那个来构造
queue<int, list<int>>que2(il);//yes
//但是不能是vector<>
//- -但是! 我用vector成功了
//原因是vector可以支持front这个操作,但是把que.front换成que.pop()就会报一堆错
//其他的list和deque说是可以使用应该是可以兼容全部的操作。so 动手实践很重要
queue<int, vector<int>>que3(ivec);
//while(!que.empty())
{
cout << que.front() << endl;
que.pop();
}
cout << que.back() << endl;
}
queue和priority_queue都在queue头文件中
priority_queue就是一种带优先级的队列,不如10个元素,它首元素是最大或最小。
操作
//q.pop()返回queue的首元素或priority_queue的最高优先级元素,不删除元素
//q.front()返回首元素或尾元素,不删除
//q.back()
//q.top()返回最高优先级的元素,不删除,只适用于priority_queue
//q.push(item)在queue末尾或priority_queue中恰当位置创建一个元素
//q.emplace(args)动态构造对象