笔记:C++学习之旅---顺序容器

STL = Standard Template Library   标准库模版
容器可以使用范围for输出或者迭代器进行输出
一个容器就是一些特定类型对象的集合。顺序容器为程序员提供了控制元素存储和访问顺序的能力。
list和forward和vector 将两个容器的设计目的是令容器任何位置的添加和删除操作都很快速。作为代价这两个容器不支持元素的随机访问:为了访问一个元素,我们只能遍历整个容器,而且,与vector、deque和array相比,这两个容器的额外内存开销都很大。(array的对象大小是固定的)。




 
构成迭代器范围的迭代器有何限制?

两个迭代器begin和end必须指向同一个容器中的元素,或者是容器最后一个元素之后的位置;而且,对begin反复进行递增操作,可以保证达到end,即end不在begin之前。

练习:
/*练习:9.4 编写函数,接受一对指向vector<int> 的迭代器和一个int值。
 *在俩个迭代器指定的范围中查找给定的值,返回一个布尔值来制定指出是否找到
 *
 *练习:9.5 重写上一题的函数,返回一个迭代器指向找到的元素。
 *注意,程序必须处理未找到给定值的情况
 */
#include
<iostream>
#include
<vector>
using
namespace
std;

//返回bool来指出是否找到
/*
bool find(vector<int>::iterator begin,vector<int>::iterator end,int value)
{
            for (auto iter = begin; iter != end; ++iter)
            {
                        if (*iter == value)
                                    return true;
                        //else
            }
            return false;
}*/
//返回一个迭代器指向找到的元素
const
vector
<
int
>::
iterator
find(
vector
<
int
>::
iterator
begin
,
vector
<
int
>::
iterator
end
,
int
value
)
//返回一个迭代器就相当于返回一个指针
{
            
for
(
auto
iter =
begin
; iter !=
end
;++iter)
            {
                        
if
(*iter ==
value
)
                                    
return
iter;
                        
//else
            }
            
return
end
;
}
int
main()
{
            
vector
<
int
> vec;
            
int
i;
            cout <<
"请输入整数到容器中:\n"
;
            
while
(cin >> i)
            {
                        
if
(i == -1)
                                    
break
;
                        vec.push_back(i);
//往容器中添加元素
            }
            
int
value;
            cout <<
"请输入要查找的元素数值\n"
;
            cin >> value;
            
if
(vec.empty())
            {
                        cout <<
"容器为空\n"
<< endl;
                        
return
-1;
            }
            
else
            {
                        cout<<find(vec.begin(), vec.end(),value)-vec.begin()<<endl;
//查找指定元素
            }
            
return
0;
}

练习9.9:begin和cbegin两个函数有什么不同?

cbegin是C++新标准引入来的,用来与auto结合使用。它返回指向容器第一个元素的const迭代器,可以用来只读地访问容器,但不能对容器元素进行修改。因此,当不需要写访问时,应该使用cbegin。

begin则是被重载过的,有两个版本:其中一个是const成员函数,也返回const迭代器;另一个则返回普通迭代器,可以对容器元素进行修改。

下面4个对象分别是什么类型?

[cpp] 
view plain
 copy
 

  1. vector<int> v1;
  2. const vector<int> v2;
  3. auto it1 = v1.begin(), it2 = v2.begin();
  4. auto it3 = v1.begin(), it4 = v2.cbegin();</span>

v1是int的vector类型,可以修改v1的内容,包括添加、删除元素以及修改元素等操作。

v2是int的常量vector类型,其内容不能修改,添加、删除元素以及修改元素值等均不允许。

it1是普通迭代器,可对容器元素进行读写访问,it2是const迭代器,不能对容器元素进行写访问。it3和it4都是const迭代器


标准库array具有固定的大小

与内置数组一样,标准库array的大小也是类型的一部分。当定义一个array时,除了指定元素类型,还要指定容器的大小:
array<int,42>      //类型为:保存42个int的数组
array<string,10>    //类型为:保存10个string的数组

为了使用array类型,我们必须同时指定元素类型和大小:
array<int,10> ::size_type i; //数组类型包括元素类型和大小;
array<int>::size_type j;      //错误:array<int>不是一个类型




练习:9.13  
如何从一个list<int>  初始化一个vector<double>?从一个vector<int> 又该如何创建?编写代码验证你的答案。
#include
<iostream>
#include
<vector>
#include
<list>
using
namespace
std;
int
main()
{
            
list
<
int
> ilst = {1,2,3,4,5,6,7,8};
            
vector
<
int
> ivc = {2,7,4,3,5,1,10,6};

            
//! from list<int> to vector<double>
            
vector
<
double
> dvc(ilst.begin(), ilst.end());
            
for
(
auto
i : ilst) cout << i;
            cout << endl;
            
for
(
auto
t : dvc) cout << t;
            cout << endl;

            
//! from vector<int> to vector<double>
            
vector
<
double
> dvc2(ivc.begin(), ivc.end());
            
for
(
auto
i : ivc) cout << i;
            cout << endl;
            
for
(
auto
t : dvc2) cout << t;

            
return
0;
}


顺序容器操作
除array外,所有标准库容器都提供灵活的内存管理。在运行时,可以动态添加或删除元素来改变大小。


练习:9.18
编写程序从标准输入读取string序列,存入一个deque中,编写一个循环,用迭代器打印deque中的元素。
#include
<iostream>
#include
<vector>
#include
<deque>
#include
<string>
using
namespace
std;

int
main()
{
            
deque
<
string
> input;
            
string
str;
            
while
(cin >> str)
            {
                        
if
(str ==
"#"
)
                                    
break
;
                        input.push_back(str);
            }
            
for
(
auto
iter = input.cbegin(); iter != input.cend(); ++iter)
            {
                        cout <<
"iter:"
<< *iter << endl;
            }
            
return
0;
}

访问元素

删除元素
与添加元素的多种方式类似,(非array)容器也有很多种删除元素的方式。



从容器内部删除元素


练习9.26:
使用下面代码定义的ia,将ia拷贝到一个vector和一个list中。使用单迭代器版本的erase从list中删除奇数元素,从vector中删除偶数元素。
int ia[] = {0,1,2,3,5,8,13,21,34,55,89};
#include
<iostream>
#include
<vector>
#include
<list>
using
namespace
std;

int
main()
{

            
int
ia[] = { 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
            
//init
            
vector
<
int
> vec(ia, end(ia));
            
list
<
int
> list(vec.begin(), vec.end());
            
//从list中删除奇元素
            
for
(
auto
lt = list.begin(); lt != list.end(); ++lt)
            {
                        
if
(*lt % 2)
                        {
                                    cout <<
"奇数:"
<<*lt << endl;
                        }
                        
else
                                    ++lt;
            }

            
//删除vector中的偶数
            
for
(
auto
vt = vec.begin(); vt != vec.end(); ++vt)
            {
                        
if
(*vt % 2 == 0)
                        {
                                    cout <<
"偶数:"
<<*vt << endl;
                        }
                        
else
                                    ++vt;
            }
            
return
0;
}

特殊的forword_list操作
当添加或删除一个元素时,删除或添加的元素之前的那个元素的后继会发生变化,为了添加或删除一个元素,我们需要访问其前驱,以便改变前驱的链接。但是forword_list是单向链表。在一个单向链表中,没有简单的方法来获取一个元素的前驱,出于这个原因,在一个forword_lisT中添加或删除元素的操作是通过改变给定元素之后的的元素来完成的,这样,我们总可以访问到被添加或删除操作所影响的元素。



练习:9.27
编写程序,查找并删除forwa
rd_list<int> 中的奇数元素。

#include
<iostream>
#include
<forward_list>
using
namespace
std;

int
main()
{
            
forward_list
<
int
> list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            
auto
prev = list.before_begin();
// 前驱,list中“首前元素”;
            
auto
curr = list.begin();
//开始位置,第一个元素;
            
while
(curr !=list.end())
            {
                        
if
(*curr % 2 == 1)
//元素为奇数
                        {
                                    curr = list.erase_after(prev);
//删除prev之后元素,即:curr指向的元素;
                        }
                        
else
                        {
                                    prev = curr;
//奇数时,将curr保存,curr自加;
                                    curr++;
                        }
                        
            }
            
for
(
auto
i : list)
                        cout <<
"i:"
<< i << endl;
            
return
0;
}


添加删除vector、string、或deque元素的循环程序必须考虑迭代器、引用和指针可能失效的问题。程序必须保证每个循环步骤中都更新迭代器、引用、或指针。如果循环中调用的是insert或erase,那么更新迭代器很容易。这些操作都返回迭代器,我们可以用来更新。


vector对象是如何增长的
为了避免每次添加新元素就执行一次内存分配和释放操作,当不得不获取新的内存空间时,vector和string的实现通常会分配比新的空间更大的内存空间。容器预留这些空间作为备用,可用来保存更多的新元素。这样,就不需要每次添加新元素都重新分配容器的内存空间了。

管理容量的成员函数


为什么list和array没有capacity?
list不能持续存储,array是固定数组大小,没有预先分配这么一说


额外的string操作


数值转换



练习9.50:
编写程序处理一个vector<string>,其元素都表示整型数值。计算vector中所有元素之和。修改程序,使之计算表示浮点值的string之和。
#include
<iostream>
#include
<string>
#include
<vector>
using
namespace
std;
int
All_Sum(
vector
<
string
> &
v
)
{
            
int
sum = 0;
            
for
(
auto
const
&s : v)
                        sum += stoi(s);
            
return
sum;
}
float
All_Float_Sum(
vector
<
string
> &
v
)
{
            
float
sum = 0.0;
            
for
(
auto
const
&s : v)
                        sum += stof(s);
            
return
sum;
}
int
main()
{
            
vector
<
string
> v = {
"1"
,
"2"
,
"3"
,
"4"
,
"5.5"
,
"6.1"
};
            cout<<
"All elements sum:"
<<All_Sum(v)<<endl;    //21
            cout<<
"ALL float elements string sum:"
<<All_Float_Sum(v)<<endl;    //21.6
            
return
0;
}

笔记:C++学习之旅---顺序容器的更多相关文章

  1. 【c++ Prime 学习笔记】第9章 顺序容器

    一个容器是特定类型对象的集合 顺序容器中元素的顺序与其加入容器的位置对应 关联容器中元素的顺序由其关联的关键字决定,关联容器分为有序关联容器和无序关联容器 所有容器类共享公有接口,不同容器按不同方式扩 ...

  2. C++学习基础四——顺序容器和关联容器

    —顺序容器:vector,list,queue1.顺序容器的常见用法: #include <vector> #include <list> #include <queue ...

  3. C++ Primer 读书笔记: 第9章 顺序容器

    第9章 顺序容器 引: 顺序容器: vector 支持快速随机访问 list 支持快速插入/删除 deque 双端队列 顺序容器适配器: stack 后进先出栈 queue 先进先出队列 priori ...

  4. 笔记-JavaWeb学习之旅19

    Redis:redis是一款高性能的NOSQL系列的非关系型数据库 NOSQL: Not Only SQL ,意即"不仅仅是SQL",是一项全新的数据库理念,泛指非关系型数据库 r ...

  5. 笔记-JavaWeb学习之旅15

    Filter:过滤器 概念:当访问服务器的资源是,过滤器可以将请求拦截下来,完成一些特殊的功能 快速入门: 步骤: 定义一个类,实现接口Filter 复写方法 配置拦截路径 package com.d ...

  6. 笔记-JavaWeb学习之旅14

    JSTL:JavaServer Pages Tag Library JSP标准标签库 if标签 <%@ page import="java.util.ArrayList" % ...

  7. 笔记-JavaWeb学习之旅11

    请求转发:一种在服务器内部的资源跳转方式 使用步骤 1.通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path) ...

  8. 笔记-JavaWeb学习之旅8

    Window对象-定时器方法 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  9. 笔记-JavaWeb学习之旅7

    JavaScript基础 概念:一门客户端脚本语言,运行在客户端浏览器中,每一个浏览器都有JavaScript的解析引擎,是一个脚本语言,不需要编译,直接就可以被浏览器解析执行. JavaScript ...

  10. 笔记-JavaWeb学习之旅5

    CP30的演示 package cn.itcast.datasourcejdbc; import com.mchange.v2.c3p0.ComboPooledDataSource; import j ...

随机推荐

  1. mysql 递归

    MySQL中实现递归查询   对于数据库中的树形结构数据,如部门表,有时候,我们需要知道某部门的所有下属部分或者某部分的所有上级部门,这时候就需要用到mysql的递归查询 1.创建表 DROP TAB ...

  2. 在为 DataGridView 添加数据列时,弹出 将要添加的列 CellType 属性为空 错误提示与说明

    事务:为 DataGridView 添加数据列[也可以说是直接操作 DataGridView 数据列...]... 原由:在为 DataGridView 添加列的时候,[至少这是第三次遇到] 弹出 添 ...

  3. 【转载】PostgreSQL逻辑订阅logical

    原文地址:https://blog.csdn.net/gguxxing008/article/details/106356086 逻辑订阅是PostgreSQL10.0开始支持的新功能,Postgre ...

  4. C# async、await、Task 探讨

    test02.ProntOut(); ///*第五题*/ static class test02 { public static async void ProntOut() { Console.Wri ...

  5. Mongodb between 时间范围

    db.getCollection("Order").find({ "Supplier.ServiceCode": "CNI", " ...

  6. 比 poi导入导出更好用的 EasyExcel使用小结

    转载请注明出处: 官方文档: https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read 1.简洁 Java解析.生成E ...

  7. 一文明白:JavaScript异步编程

    同步和异步 JS是单线程 JavaScript语言的一大特点是单线程,同一时间只能做一件事 (单线程的JS 就是一个傻子,脑子一根筋,做着当前的这件事情,没有完成之前,绝对不会做下一件事情) 当然,这 ...

  8. Activiti7开发(五)-我的审批历史

    查看本人审批过的历史 public AjaxResult historyFromData(@RequestParam(value = "businessKey",required ...

  9. 几个对js帮助挺多的大佬写的博客

    深入理解javascript原型和闭包(完结) JavaScript系列文章 同步异步回调DEMO知乎大佬的this与new解释 宏任务与微任务解析 js闭包 Vue项目中技巧ts学习 ES6基础入门 ...

  10. 我没能实现始终在一个线程上运行 task

    前文我们总结了在使用常驻任务实现常驻线程时,应该注意的事项.但是我们最终没有提到如何在处理对于带有异步代码的办法.本篇将接受笔者对于该内容的总结. 如何识别当前代码跑在什么线程上 一切开始之前,我们先 ...