把《c++ primer》读薄(3-2 标准库vector容器+迭代器初探)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正。
标准库vector类型初探,同一种类型的对象的集合(类似数组),是一个类模版而不是数据类型,学名容器,负责管理 和 存储的元素 相关的内存,因为vetcor是类模版,对应多个不同类型,比如int,string,或者自己定义的数据类型等。
程序开头应如下声明
#include <iostream>
#include <vector>
#include <string>
using std::string;
using std::vector;
using std::cout;
using std::cin;
using std::endl;
简单的vector<xx>类型的变量声明
vector<int> ivec;//声明一个vector<int>数据类型的变量ivec
问题1、标准库类型vector初始化的值的类型必须一致!
vector<int> ivec1;//默认调无参构造
vector<int> ivec2(ivec1);//直接初始化ivec2为ivec1的一个副本
vector<string> strvec(ivec2);//error C2514: “std::vector”: 类没有构造函数。说明类型不一致,无法完成初始化!自然报错!
vector对象的初始化,vector类模版定义了四个构造函数,无参构造函数,直接初始化的带参构造函数,初始化为n个值为i的构造函数,还有一种值初始化构造函数。
下面的也没有问题!
vector<vector<int>> ivec;//ok,完全没问题!存储的是vector<int>类型的元素
问题2、勿忘它的两种直接初始化的方式
vector<int> ivec(, );//初始化ivec为含有20个元素,每个元素=10
vector<string> svec(, "hello");//初始化为含义10个元素,每个元是一个字符串hello //值初始化方式
vector<int> ivec1();//内置类型,比如int类型的元素存在容器vector,那么默认初始化为100个0
vector<string> svec1();//同理若是类类型,如果有默认构造函数,那么按照它的默认构造函数初始化,比如这里是20个空串
如果既不是带默认构造函数的类类型,也不是c/c++的内置基本类型,那么编程时,需要手动写上初始化值具体是多少。还有一种极端,类类型里没有定义任何的构造函数,那么c++标准库还是会产生一个初始值去依次初始化容器里的元素。
问题3、需要理解c++标准库容器对象,比如vector容器的一个重要属性!
标准库容器在运行的同时,可以高效的被添加元素,且不用预先分配内存空间!要知道,vector动态增长的效率高,专家推荐使用,且要知道,这不同于内置基本类型,后续深入,这里要先记住,不需要提前为容器对象分配内存。
问题4、对vector容器对象的求长度和判空操作
发现十分类似标准库string对象的操作,还有数组等,很像的。但是肯丢有不同。这里要注意,对于容器vector来说,vector类型总是要说明它包含的元素的类型!不能丢!
vector<string> svec(, "null");
//vector::size_type len = svec.size();//error C2955: “std::vector”: 使用类 模板 需要 模板 参数列
cout << len << endl;
改为
vector<string>::size_type len = svec.size();
cout << len << endl;//打印10
判空操作(和标准库类型string的判空类似,空就返回true)
vector<int> ivec;
if (ivec.empty())
{
cout << ivec.size() << endl;//成功执行,打印0
}
问题5、对vector容器添加元素的操作push_back()
vector<string> svec;
string str;
//每次循环把输入的str字符串插入到vector容器对象的后面
while (cin >> str)
{
svec.push_back(str);
}
//直到循环结束为止
问题6、vector容器对象的下标操作(类似string对象的下标)
可以作为右值,也可以作为左值,同时建议使用标准库容器的size_type类型来定义下标
vector<string> svec(, "sss");
//重置容器内部元素的值为ooxx
for (vector<string>::size_type i = ; i != svec.size(); i++)
{
svec[i] = "ooxx";
}
需要知道的事实:类似size()这样的小型库函数,在c++里都被定义为了内联函数!
注意:vector容器的下标操作(标准库string类型同样类似),仅仅是只能获取已经存在的元素,不能添加元素!如下是错误的
vector<int> ivec;
for (vector<int>::size_type i = ; i != ; i++)
{
//ivec[i] = i;//这样做是错误的!程序中断!因为ivec是空的vector对象!下标操作只能针对已经存在的元素
ivec.push_back(i);//这样就对了,从尾部插入
}
且要知道,vector容器的下标也是类似string对象或者数组,从0开始
vector<int> ivec(, );
//int i = ivec[10];//产生运行时错误,程序中断,不存在元素下标为10的
这样的错误,就是常见的缓冲区溢出错误,很常见,需要注意,对数组也适用,还有标准库string类型
问题7、输入一组整数到vector对象,相邻的元素相加输出,并提示奇数的情况。
错误1:goto语句造成死循环
int in;
vector<int> ivec; begin:
cout << "请输入一组整数:注意ctrl+z结束输入!" << endl; while (cin >> in)
{
ivec.push_back(in);
}
//判断空输入否
if ( == ivec.size())
{
cout << "输入为空,重新输入!" << endl;
goto begin;
}
错误2:下标溢出错误
for (vector<int>::size_type i = ; i < ivec.size(); i = i + )
{
cout << ivec[i] + ivec[i + ] << "\t";
//每行输出的个数控制为5个。必须i+1,因为i初始值=0
if ( == (i + ) % )
{
cout << endl;
}
}
i<ivec.size()这里出错,下标溢出,只有元素个数是偶数的时候不错,奇数就溢出了。修改后:
#include <iostream>
#include <vector>
#include <string>
using std::string;
using std::vector;
using std::cout;
using std::cin;
using std::endl; int main(void)
{
int in;
vector<int> ivec;
cout << "请输入一组整数:注意ctrl+z结束输入!" << endl; while (cin >> in)
{
ivec.push_back(in);
}
//判断空输入否
if ( == ivec.size())
{
cout << "输入为空,重新输入!" << endl;
system("pause");
return -;
}
//求相邻的元素的和,关键算法!
//因为是相邻元素,故判断完毕,i+2之后再赋给i,跨度为2
//且不知道输入的元素是奇数,肯丢留最后一个元素不计算,是偶数则正好计算完毕!
//这里必须是i < size -1,否则下标溢出错误,偶数没问题,主要是奇数的话,如果到倒数第二个元素还+2,必然是溢出错误!
for (vector<int>::size_type i = ; i < ivec.size() - ; i = i + )
{
cout << ivec[i] + ivec[i + ] << "\t";
//每行输出的个数控制为5个。必须i+1,因为i初始值=0
if ( == (i + ) % )
{
cout << endl;
}
}
//判断奇偶
if ( != ivec.size() % )
{
cout << "元素个数为奇数,最后一个元素" << ivec[ivec.size() - ] << "被忽略!" << endl;
} cout << endl; system("pause");
return ;
}


问题8、读入一组整数到vector对象,使得头尾元素两两配对,计算和,并输出奇数元素个数的提示。
vector<int> ivec;
int in;
cout << "输入一组正数,ctrl+z结束输入:" << endl; while (cin >> in)
{
ivec.push_back(in);
} if ( == ivec.size())
{
cout << "空容器,必须输入元素!" << endl;
system("pause");
return -;
}
//首末元素相加的处理,具有模版性质,也就是设计程序的通用性
//如果是奇数元素,那么中间的会留下,如果是偶数元素,没这个问题
vector<int>::size_type first = ;
vector<int>::size_type last = ;
vector<int>::size_type count = ;//计数,控制打印输出 for (first = , last = ivec.size() - ; first < last; first++, last--)
{
cout << ivec[first] + ivec[last] << "\t";
count++;
//控制打印输出每行3个数
if ( == count % )
{
cout << endl;
}
}
//判断奇数还是偶数,给出提示
//灵活!使用简单的方式,如果是奇数的话,必然最后last和first重合
if (first == last)
{
cout << "中间的元素" << ivec[first] <<"留下了,因为元素个数是奇数!" << endl;
} cout << endl;


问题9、读入一段文本到vector对象,把对象中每个元素里面的单词都转换为大写之后在输出,5个一行。
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
using std::string;
using std::vector;
using std::cout;
using std::cin;
using std::endl; int main(void)
{
vector<string> svec;
string str; while (cin >> str)
{
svec.push_back(str);
} if ( == svec.size())
{
return -;
} for (vector<string>::size_type i = ; i != svec.size(); i++)
{
for (string::size_type j = ; j != svec[i].size(); j++)
{
//如果是小写字母
if (islower(svec[i][j]))
{
//转换为大写输出
svec[i][j] = toupper(svec[i][j]);
}
} cout << svec[i] << "\t"; if ( == (i + ) % )
{
cout << endl;
}
} system("pause");
return ;
}

问题10、迭代器入门
//c++为每一种标准容器都定义了一种迭代器类型,迭代器是一种——可以检测容器内的元素并且可以遍历元素的数据类型。除了使用下标来访问vector对象之外,还可以使用迭代器访问,比下标操作更方便,更通用,因为所有的标准库的容器都支持迭代器,但是只有部分容器支持下标操作,故成熟的c++程序员应该使用迭代器,而不是下标操作访问容器内的元素。
//vector容器的迭代器类型
vector<int>::iterator iter;//定义一个iter变量,它的数据类型是vector<int>定义的迭代器类型
//记住,每个标准库容器都定义了自己的迭代器类型,用来遍历自己容器内的元素。
//每个容器都定义了begin和end函数,目的是返回迭代器,如果容器不空,则begin返回的迭代器指向容器内的第一个元素
vector<int> ivec;
vector<int>::iterator iter;
iter = ivec.begin();//iter变量被初始化,使用容器的begin函数返回的迭代器,此时iter指该元素为ivec[0]
//恰恰相反,end函数返回的迭代器,指向容器内末端元素的下一个元素!记住是下一个!不是最后一个!故end操作返回的也叫超出末端迭代器。说明end函数返回的迭代器指向一个不存在的元素,不指向容器内任何实际存在的元素!作用是哨兵!表示我们已经处理完毕容器所有元素!
如果容器为空,则begin函数返回的迭代器和end函数返回的迭代器相同。
问题11、vector迭代器的自增、解引用、和比较相等否的操作
如果想要获取迭代器指向的元素的值,可以使用类似指针的操作,解引用操作!*iter就代表迭代器iter指向的元素的值!比如iter迭代器指向的元素是容器内第一个ivec[0],那么*iter和ivec[0]是等价语句!
如果想让迭代器类似下标那样,移动自己的指向,则可以使用迭代器的自增操作。比如iter++就是迭代器向前移动一个元素的位置!除了这些,迭代器也可以进行比较操作,==、!=操作来比较容器的迭代器,如果两个迭代器指向同一个元素,则==为真,否则为假。
注意,end函数不指向容器内的元素,故不能对它使用自增或者解引用操作!
//新的赋值方式
vector<int> ivec(, );
//把容器ivec的元素重置为0 //for (vector<int>::size_type i = 0; i != ivec.size(); i++)
//{
// ivec[i] = 0;//这是old方法
//} //比较经典常用的方法如下:
for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); iter++)
{
*iter = ;
}
如果容器为空,则begin函数和end函数返回的迭代器相等,for循环测试失败,没问题!
问题12、两类只读迭代器类型
//类似普通的const常量,但是有区别。比如,如果仅仅想遍历容器的元素,对迭代器有const_iterator类型的迭代器,对它解引用,得到的是指向const对象的引用
string str;
vector<string> svec; while (cin >> str)
{
svec.push_back(str);
} for (vector<string>::const_iterator iter = svec.begin(); iter != svec.end(); iter++)
{
//*iter = "dada";//error,const_iterator类型的迭代器,本身可以被改变,比如自增,但是迭代器指向的容器内的元素不能被修改!
//和普通的const常量有一些区别!有些类似指向常量的指针,指针本身可以变,但是指向的内容不能修改。
}
来对比const类型的迭代器,类似常指针,本身定义的时候必须初始化,本身不能被修改,但是指向的内容可以修改,如
vector<int> ivec();
//const类型的iter必须初始化
const vector<int>::iterator iter = ivec.begin();
//初始化之后不能被修改
//iter++;//error
//但是iter指向的内容可以被修改,对比,const_iterator类型的迭代器,类似指向常量的指针,迭代器本身能修改,指向的元素不能修改,,和他/她相反
*iter = ;
注意区分两者,不要混淆。总结:
const 迭代器是迭代器常量
该迭代器本身的值不能修改,即该迭代器在定义时需要初始化,而且初始化之后,不能再指向其他元素。若需要指向固定元素的迭代器,则可以使用const 迭代器。但是它指向的元素的值可以被修改!
const_iterator 是一种迭代器类型
对这种类型的迭代器解引用会得到一个指向const 对象的引用,即通过这种迭代器访问到的对象是常量。该被指向的对象不能修改,因此,const_iterator 类型只能用于读取容器内的元素,不能修改元素的值。若只需遍历容器中的元素而无需修改它们,则可以使用const_iterator。但是迭代器本身能被修改。两者相反!
问题13、迭代器的算术操作
其他容器的迭代器类似。除了自增、自减之外,还有其他算术运算适用。
vector<int> ivec();
//const类型的iter必须初始化
vector<int>::iterator iter = ivec.begin();
//iter++;//ok
//iter--;//ok //iter + 5;//ok,对一个迭代器对象加一个整型值,使得iter指向新的元素第6个元素,注意不要越界
//但是,加上或者减去的整型,最好是size_type类型的! //iter - 100;//error,越界,程序中断! //iter + 10;//ok,加的时候,可以加到最后一个元素的下一位。 //还能求两个迭代器的距离
vector<int>::iterator iter1 = ivec.end(); cout << iter1 - iter << endl;//打印10 //注意,这里相减得到的值,可能是负数,也可以是正数
cout << iter - iter1 << endl;//打印-10
//这就说明,这个值的类型不再是容器的size_type类型,而是新的容器的类型,叫:
//differenec_type
vector<int>::difference_type i = iter - iter1;//ok,是带符号类型
注意,迭代器是没有相加操作的!比如:
iter1 + iter1;//报错!
也就是说,要求得容器的中间元素,不能这样写:
vector<int>::iterator mid = (vi.begin() + vi.end())/;
但是可以这样:
vector<int>::iterator mid = vi.begin() + vi.end()/;
直接定位容器的中间元素,简单高效,不再需要一次次的去自增或者自减的遍历了。
欢迎关注
dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

把《c++ primer》读薄(3-2 标准库vector容器+迭代器初探)的更多相关文章
- C++ Primer 有感(标准库vector及迭代器)
vector是同一种对象的集合,每个对象都有一个对应的整数索引值.和string对象一样,标准库将负责管理与存储元素相关的类存.引入头文件 #include<vector> 1.vecto ...
- C++标准库vector以及迭代器
今天看C++的书,出现了一个新的概念,容器vector以及容器迭代器. vector是同一种对象的集合,每个对象都有一个对应的整数索引值.和string对象一样,标准库将负责管理与存储元素相关的类存. ...
- C++标准库vector及迭代器
vector是同一种对象的集合,每个对象都有一个对应的整数索引值.和string对象一样,标准库将负责管理与存储元素相关的类存.引入头文件 #include<vector> 1.vecto ...
- 把《c++ primer》读薄(3-3 标准库bitset类型)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. //开头 #include <bitset> using std::bitset; 问题1.标准库bitset类型( ...
- C++ Primer 第三章 标准库类型vector+迭代器iterator 运算
1.vector: 标准库类型vector表示对象的集合,其中所有对象的类型都相同,集合中的每个对象都有一个与之对应的索引,索引用于访问对象.因为vector“容纳着”其他对象,所以它也常被称作容器( ...
- C++primer第三章标准库类型
除第二章介绍的基本数据类型外,C++ 还定义了一个内容丰富的抽象数据类型标准库. 本章将介绍标准库中的 vector.string 和 bitset 类型. string 类型支持长度可变的字符串 v ...
- c++ primer 第三章 标准库类型
1. string 标准库 1.1初始化 string s1; 默认构造函数s1为空 string s2(s1); 将s2初始化为s1的一个副本 string s3("value" ...
- c/c++ 标准库 vector
c/c++ 标准库 vector 标准库 vector的小例子 test1~test7 #include <iostream> #include <vector> using ...
- 把《c++ primer》读薄(3-1 标准库string类型初探)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1:养成一个好习惯,在头文件中只定义确实需要的东西 using namespace std; //建议需要什么再using声 ...
随机推荐
- nginx 各类网站设置 (laravel , thinkphp , nodejs , https)
基础部分设置 [root@centos ~]# vim /opt/nginx/conf/nginx.conf user www www;worker_processes auto;pid logs/n ...
- android——自定义listView
都知道微信主机面 有个界面会一行一一行的聊天记录,那个效果就可以用listview来实现(当然这只是其中的一种) listView是一种比较常见的组件它用来展示列的view,它是根据数据的长度来显示数 ...
- sql查询
结果集是 id name 1 张三2 张三3 李四4 王五5 王五我想查询出有多少不重名的人的数量,并显示在每一行结果集里面结果如下:num id name 3 1 张三3 2 ...
- .net core中使用openssl的公钥私钥进行加解密
这篇博文分享的是 C#中使用OpenSSL的公钥加密/私钥解密 一文中的解决方法在 .net core 中的改进.之前的博文针对的是 .NET Framework ,加解密用的是 RSACryptoS ...
- 移动端手势库hammerJS 2.0.4官方文档翻译
hammerJS是一个优秀的.轻量级的触屏设备手势库,现在已经更新到2.04版本,跟1.0版本有点天壤地别了,毕竟改写了事件名并新增了许多方法,允许同时监听多个手势.自定义识别器,也可以识别滑动方向. ...
- JS原型继承和类式继承
前言 一个多月前,卤煮读了一篇翻译过来的外国人写的技术博客.此君在博客中将js中的类(构造)继承和原型继承做了一些比较,并且得出了结论:建议诸位在开发是用原型继承.文中提到了各种原型继承的优点,详细的 ...
- 《CLR.via.C#第三版》第二部分第13章节 接口 读书笔记(七)
这章的书写感觉很普通,是些基础的认知知识. 其中一点的重要认知,泛型接口的好处(其实也是使用泛型的好处之一):编译时类型安全&处理值类型时减少装箱. 再说点书上没有的.本来这些知识我打算另外分 ...
- SQL Server 事务隔离级别详解
标签: SQL SEERVER/MSSQL SERVER/SQL/事务隔离级别选项/设置数据库事务级别 SQL 事务隔离级别 概述 隔离级别用于决定如果控制并发用户如何读写数据的操作,同时对性能也有一 ...
- 如何调试ANDROID下面黑屏问题
最近很多朋友在问,为毛在WINDOWS下对了,跑ANDROID的虚拟机或者真机就黑屏了, 有的是只有FPS信息,有的是连FPS信息都没有.如果是程序能够正常启动,不会闪退,但显示不对. 那十有八九都是 ...
- Azure PowerShell (2) 修改Azure订阅名称
<Windows Azure Platform 系列文章目录> Update: 2016-01-11 笔者文档主要都是用Azure PowerShell 0.x版本来实现的,比如0.98版 ...