1.概述
#include //迭代器,包含c++11的begin() 和end()函数
#include //c++11 数组类型,长度固定,提供了更好、更安全的接口,执行效率和内置数组相同,可以有效替代内置数组
#include //c++11 值类型的数组类型,针对值类型的数组,有更多的操作,比如求和,最大最小数等。
#include //双向链表,插入删除速度快,不支持随机访问
#include //c++11 单向链表,单向访问,插入删除速度快,不支持随机访问,没有size操作
#include //双端队列,支持快速随机访问
#include //string类型,插入删除耗时
#include //迭代器类型,插入删除耗时
#include
/*
* 具体需要哪个容器根据它的数据结构的优势来选择
* C++11 vector> “>>”不用添加空格了
*/
如果begin和end相等,则范围为空
如果begin和end不等,则范围至少包括一个元素,且begin指向范围中的第一个元素
while (begin ! = end) {
*begin = val;//正确,范围非空
++begin;
}
listil = {1,2,3,4,5,6,7};
list::iterator it = il.begin(), it2 = il.end();
while(it < it2) //error
{ ...}
容器类形成员
size_type、iterator、const_iterator
//iil是通过list定义的一个迭代器类型
list::iterator iil = il.begin();
c++11新增加了auto和begin,end的结合用法。
增加了cbegin和crbegin。
#include
#include
#include
using namespace std;
int main()
{
listil = {"hello", "world", "wang", "wei", "hao" };
auto it1 = il.begin(); //list::iterator
auto it2 = il.cbegin(); //list::const_iterator
auto it3 = il.rbegin(); //list::reverse_iterator
auto it4 = il.crbegin(); //list::const_reverse_iteratror
cout << *it1 << endl;
//*it2 = "ww"; error:const类型不能修改
cout << *it2 << endl;
cout << *it3 << endl;
cout << *it4 << endl;
//*it4 = "ww"; error:const类型不能修改
}
容器定义和初始化
#include
#include
using namespace std;
int main()
{
vectorivec1; //默认初始化
vectorivec2 = {1,2,3,4,5,6,7,8,9,0}; //列表初始化
//vectorivec2{1,2,3,4,5,6,7,8,9,0};
vectorivec3(ivec2); //拷贝初始化
//vectorivec3 = ivec2;
vectorivec4(ivec1.begin(), ivec1.end());//迭代器初始化
vectorivec5(10); //n个元素初始化
vectorivec6(10, 9); //n个元素加初始值初始化为9
return 0;
}
为了创建一个容器为另一个容器的拷贝,两个容器的类型及其元素类型必须匹配
不过当传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的。而且新容器
和原容器中的元素类型也可以不同,!只要能将拷贝的元素转换即可
#include
#include
#include
#include
#include
using namespace std;
int main()
{
listil1 = {"hello", "world", "hehe"};//新标准中可以对一个容器进行列表初始化
listil2(il1);
for(const string&s : il2)
cout << s << endl;
//dequedeq(il1);
dequedeq(il1.begin(), il1.end());
for(const string&s : deq)
cout << s << endl;
}
.标准库array具有固定大小
array类型是c++11数组类型,长度固定(必须初始的时候指定),提供了更好,更安全的接口,
执行效率和内置数组相同,可以有效替代内置数组
标准库array的大小也是类型的一部分,当定义一个array时,除了指定元素类型,还要指定容器大小
#include
array a = {1,2,3,4,5,6,7,8,9,0};
array //保存10个string的数组
array b = a;
赋值和swap
如果两个容器原来大小不同,赋值运算后两者的大小与右边容器的原大小相同
与内置数组不同,标准库array类型允许赋值,赋值号左右两边的运算对象必须具有相同的类型
使用assign(仅顺序容器)
允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值
#include
#include
using namespace std;
int main()
{
vectorivec;
ivec = {1,2,3,4,5,6,7,8};
for(const int &i: ivec)
cout << i << endl;
vectorivec2 = {9,9,9};
ivec = ivec2; //直接赋值
for(const int &i: ivec)
cout << i << endl;
vectorivec3 = {1,1,1,1,1};
vectorivec4 = {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 svec1(10);
vector svec2(24);
swap(svec1,svec2);
}
交换两个容器保证会很快,元素本身并为交换,swap只是交换了两个容器的内部结构。
意味着指向容器的迭代器,引用,指针在swap操作之后都不会失效
容器大小操作
size( ):返回容器中元素的个数
empty( ):查看容器是否为空,size=0时返回true
max_size( ):返回一个大于或等于该容器所能容纳最大元素数的值
forward_list不支持size( )操作。
关系运算符
每个容器都支持(==和!=),除了无序关联容器所有容器都支持(>、<、>=、<=)
关系运算符左右两端必须容器类型相同。
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器。
3、顺序容器操作添加元素
#include
#include
#include
#include
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 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 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 ivec2 = { 1 };
auto iter2 = ivec2.begin();
ivec2.insert(iter2, 3, 10); //c.insert(p, n, t) 迭代器p位置插入n个t元素
vector 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)范围内的元素
vectorivec4;
//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
#include
using namespace std;
int main()
{
vectorivec = {1,2,3,4,5,6,7,8,9,0};
//vectorivec;
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
#include
#include
using namespace std;
int main()
{
int ia[] = {1,2,3,4,5,6,7,8,9,0};
vectorivec;
listil;
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
#include
#include
#include
using namespace std;
int main()
{
forward_listft = {1,2,3,4,5,6,7,8,9,0};
vectorivec = {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( );
curr = ft.erase_after(n);//删除p指向位置后的元素,返回被删元素之后的元素迭代器
c.resize(n) :调整c大小为n个元素。若n c.resize(n,t) :调整c大小为n个元素。任何新添加的元素都初始值为t。 !如果缩小容器,则指向被删除元素的迭代器,引用和指针都会失效。对vector,string,deque,resize可能导致迭代器,指针引用失效。 !如果容器保存的是类类型的元素,向容器添加元素时我们必须提供初始值,或元素必须提供默认构造函数。 容器操作可能使迭代器失效
是否会使迭代器失效还要看迭代器具体使用什么数据结构实现的。 比如forward_list这个容器就比较特殊,insert( )返回的是插入元素位置的迭代器。删除erase( p )删除的是p的下一个位置的元素,返回的是删除元素下一个位置的迭代器。 因为它是由单向链表实现,指针是单向的,我们不能返回去访问先前的元素,那么删除必须指定的元素必须要保存prev先前的迭代器,不然无法实现删除。 所以每个容器具体实现的数据结构是重点。 一个例子 复制容器中元素值是奇数的,删除容器中的元素值是偶数的。 好好体会一下,这些容器的差别 迭代器失效了我们却没有保存iter。 !注意: 1)、对于insert来说,除了forward_list插入是后插外,其他容器是前插,前插返回前插的元素的迭代器,记得保存,插入后迭代器会失效。后插返回的是后插元素的迭代器。 总之不管插入位置,只要插入返回的就是插入的元素的迭代器,但是要考虑它的位置是前插还是后插。 2)、管理迭代器,确认每次改变容器的操作之后都正确的重新定位迭代器。这个对vector,string,deque尤其重要。 3)、记不起来时想下它的数据结构。 4、vector对象如何增长 标准库实现者采用了可以减少容器控件重新分配次数的策略,当不得不获取新内容空间时,vector和string通常会分配比新的需求更大的内存空间,以作备用。
构造string的其他方法
每个搜索都返回一个string::size_type值,表示匹配发生位置的下标。如果搜索失败,则返回一个string::npos的static成员 string的搜索操作
s.find(args) 查找args第一次出现的位置 s.rfind(args) 查找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无默认值 一般的find都是从左到右,rfind提供了从右到左。
compare函数: 等于大于小于返回0,正数或者负数 compare的6个版本如下 标准引入了多个函数,可以实现数值数据与标准库string之间的转换 100 int:1000long:1000unsigned long:1000longlong:1000unsigned longlong:1000 float:1000.11double:1000.11long double:1000.11 6、容器适配器 container_type是实现适配器的底层容器类型 三种:stack, queue, priority_queue 本质上:适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样 一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型 理解:容器适配器是基于已有的容器做的功能上的一种改进。 定义一个适配器: 每个适配器都有两个默认构造函数:默认构造函数创建一个空对象,接受一个容器的构造函数拷贝该容器来初始化适配器。 因为所有适配器都要求容器具有添加、删除以及访问尾元素的能力,所以array和forward_list不能构造适配器 1.stack stack:一种元素先进后出的一种容器 2.queue queue:一种元素先进先出的容器 queue可以由list和deque来构造,但是不能由vector来构造,默认是deque构造
给容器中每个元素后面插入值
#include
#include
如果把iter = ivec.insert(iter, 1)写成ivec.insert(iter, 1)程序会出很大的错误。
#include
5、额外的string操作
#include
改变string的其他方法
#include
#include
string搜索操作
//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;
}
s.find_first_of(args) 在s中查找args任何一个字符第一次出现的位置
逆向搜索:
#include
数值转换:
#include
#include
结果为:
return 0;
}
注意:
deque
stack可以使用除了array和forward_list类型的其他任何类型的容器来构造
#include
#include
#include