C++ primer学习笔记--第9章(2)

顺序容器

1.顺序容器操作

前面介绍的那些是所有容器都支持的,我们接下来介绍的只适用于顺序容器(以后还会介绍关联容器)。

(1)添加元素

不知道为啥原书篇幅超多,我觉得直接看代码就很明了,所以我就写代码了:

list<int> a = {1, 2, 3}; //注释为a的元素内容{1, 2, 3}
a.push_back(4); //{1, 2, 3, 4},array和forwar_list不支持
a.push_front(0); //{0, 1, 2, 3, 4},只有list,forward_list和deque支持

以上两种可以方便地在头尾添加元素,更进一步,insert允许我们在容器任意位置中插入多个元素,vector,deque,list,string都支持insert:每个insert接受一个迭代器作为第一个参数:

list<string> slist = {Jay"};
auto iter = slist.begin();
slist.insert(iter, "Hello"); //将Hello添加到iter之前的位置

//insert还有重载版本
vector<string> svec;
svec.insert(svec.end(), 10, "May"); //在末尾插10个May
vector<string> a = {"1", "2"};
slist.insert(slist.begin(), a.end()-1, a.end()); //插入了"2"
slist.insert(slist.begin(), slist.begin(), slist.end());
//错误,要拷贝的迭代器不能指向自己

现在我要在vector中实现一个功能,每次插入的单词都插在头部:

vector<string> a;
auto it = a.begin();
string word;
while(cin >> word)
{
it = a.insert(it, word);
}

因为insert会返回当前位置,调用insert会在前一个位置插入,相当于调用了push_front

新标准还引入了三个新成员-emplace_front,emplace和emplace_back,这些操作时构造元素,不是拷贝元素,它们仨分别于push_front,insert和push_back对应:

vector<Sales_data> c;
c.emplace_back("1", 25, 16.8);
//在c的末尾构造一个Sales_data对象(调用三个参数的构造函数)

c.emplace_back("1", 25, 16.8); //这样不对的
c.emplace_back(Sales_data("1", 25, 16.8)) //这样可以
(2)访问元素

每个顺序容器都有一个front函数,返回首元素的引用;除forward_list之外的都有一个back,返回尾元素的引用,还是看代码好理解:

vector<string> c = {"1", "2"};
if(!c.empty)
{
auto v1 = *c.begin(), v2 = c.front(); //v1,v2都是首元素的拷贝

auto last = c.end();
auto v3 = *(--last); //除forward_list之外
auto v4 = c.back(); //也除forward_list之外
}

c[n]和c.at(n) 返回下标为n的元素的引用:

if(!c.empty)
{
auto &v = c.back(); //获得尾元素的引用
v = “3”; //改变了c中的元素
auto v2 = c.back();
v2 = "0"; //没改c的元素,是个引用
}

与以前一样,如果用auto变量保存这些函数的返回值,并且要改变容器内的值,必须要将变量定义为引用类型。 下标一定要在合理范围内,这个程序员自己要注意,来个错误示范:

vector<int> a;
cout << a[0]; //错了
(3)删除元素
vector<int> a = {1, 2, 3, 4, 5, 6};
a.pop_front(); //删除首元素
a.pop_back(); //删除尾元素

//删除a中所有奇数
auto it = a.begin();
while(it != a.end())
{
if(*it % 2)
{
it = a.eraser(it); //删除奇数
}
else
{
++it;
}
}

//删除所有元素,两种方式
a.clear();
a.eraser(a.begin(), a.end());
(4)内存管理

vector对象是如何增长的

这一部分我们来仔细研究一下vector在内存中的管理。
我们已经学过,vector的元素在内存中是连续存储的,这样一来,如果我在添加元素的时候,没有空间去容纳新元素应该怎么办呢?vector是这么做的:

  1. 把已有元素移动到新空间中
  2. 然后添加新元素
  3. 释放旧存储空间 那么问题又来了,我们的新空间要分配多大呢?一般来说貌似是旧空间的两倍,反正就是会预留一些空间。
管理容量的成员函数
成员函数作用
c.size()目前含有元素的数量
c.capacity()所能保存的最大元素数量(不重新分配内存空间),只适用vector和string
c.shrink_to_fit()将capacity()减小为size()相同大小(适用vector、string、deque)
c.reserve(n)分配至少能容纳n个元素的内存空间

reserve():只有当当前内存空间不够用的时候,reserve才会重新分配内存空间(可能比n还要大),够用的话这个函数什么也不做,这样的话,reserve永远不会减少容器所占用的内存空间。

还有一个resize函数已经用过的,它只改变容器中元素的个数,不会改变容器的容量。

capacity和size

直接看代码:

vector<int> ivec;
ivec.push_back(1);
cout << ivec.size() << ivec.capacity();

size肯定是1,capacity依赖于标准库的具体实现,肯定大于等于1


(5)改变容器大小

除了array之外,我们可以用resize来改变容器大小:

  • 如果当前大小大于所要求的的大小,容器后面的元素会被删除
  • 如果当前大小小于新大小,会将新元素添加到容器后部
    list<int> a(10, 42); //10个42
    a.resize(15); //后面再加5个0,是默认初始化的,
    //如果是类类型,要么就有默认构造函数,要么就提供初始值
    a.resize(25, -1); //后面再加10个-1
    a.resize(5); //从末尾删除20个元素,就剩5个42
    缩小容器,指向被删除元素的迭代器、引用和指针都会失效;对vector、string和deque进行resize也可能导致它们失效。
容器操作可能使迭代器失效

使用失效的指针、引用或迭代器是很严重的错误,我们来仔细分析一下,分添加和删除两种情况
添加元素:

  • 容器是vector或string:
  • 如果存储空间被重新分配,则指针等全部失效
  • 未重新分配,插入位置之后的那些都失效
    总之就是内存位置变了就不行了
  • 对于deque,插入除了首尾之外的位置就会失效;如果在首尾添加,迭代器失效,引用和指针不会失效
  • 对于list和forward_list,都还是有效的

删除元素:
(被删除的元素对应的那三样肯定挂了)

  • 对于list和forward_list都有效
  • 对于deque,
  • 如果在首尾之外的任何位置删除,都失效;
  • 如果删除尾元素,尾后迭代器失效,其他不影响,如果删除首元素
  • 如果删除首元素,都不受影响
  • 对于vector和string,被删除元素之前的都有效,所以尾后迭代器总会失效
注意函数返回迭代器的位置

我们来写一个函数,删除偶数元素,复制每个奇数元素:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto iter = vi.begin();
while(iter != vi.end())
{
if(*iter % 2)
{
iter = vi.insert(iter, *iter);
iter += 2; //跳过当前元素和复制元素
}
else
{
iter = vi.eraser(iter); //删除偶数元素
//不用向前移动迭代器,因为eraser返回删除元素的下一个
}
}
不要保存end返回的迭代器
vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
auto end = vi.end();
while(begin != end)
{
begin = v.insert(begin, 42);
++begin; //跳过刚刚加入的元素
}

有问题的,因为end记住了一个以前的end,更好的应该这样:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
while(begin != vi.end())
{
begin = v.insert(begin, 42);
++begin; 
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值