把《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(20, 10);//初始化ivec为含有20个元素,每个元素=10
    vector<string> svec(10, "hello");//初始化为含义10个元素,每个元是一个字符串hello

    //值初始化方式
    vector<int> ivec1(100);//内置类型,比如int类型的元素存在容器vector,那么默认初始化为100个0
    vector<string> svec1(20);//同理若是类类型,如果有默认构造函数,那么按照它的默认构造函数初始化,比如这里是20个空串

如果既不是带默认构造函数的类类型,也不是c/c++的内置基本类型,那么编程时,需要手动写上初始化值具体是多少。还有一种极端,类类型里没有定义任何的构造函数,那么c++标准库还是会产生一个初始值去依次初始化容器里的元素。

问题3、需要理解c++标准库容器对象,比如vector容器的一个重要属性!

标准库容器在运行的同时,可以高效的被添加元素,且不用预先分配内存空间!要知道,vector动态增长的效率高,专家推荐使用,且要知道,这不同于内置基本类型,后续深入,这里要先记住,不需要提前为容器对象分配内存。

问题4、对vector容器对象的求长度和判空操作

发现十分类似标准库string对象的操作,还有数组等,很像的。但是肯丢有不同。这里要注意,对于容器vector来说,vector类型总是要说明它包含的元素的类型!不能丢!

    vector<string> svec(10, "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(10, "sss");
    //重置容器内部元素的值为ooxx
    for (vector<string>::size_type i = 0; i != svec.size(); i++)
    {
        svec[i] = "ooxx";
    }

需要知道的事实:类似size()这样的小型库函数,在c++里都被定义为了内联函数!

注意:vector容器的下标操作(标准库string类型同样类似),仅仅是只能获取已经存在的元素,不能添加元素!如下是错误的

    vector<int> ivec;
    for (vector<int>::size_type i = 0; i != 100; i++)
    {
        //ivec[i] = i;//这样做是错误的!程序中断!因为ivec是空的vector对象!下标操作只能针对已经存在的元素
        ivec.push_back(i);//这样就对了,从尾部插入
    }

且要知道,vector容器的下标也是类似string对象或者数组,从0开始

    vector<int> ivec(10, 1);
    //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 (0 == ivec.size())
    {
        cout << "输入为空,重新输入!" << endl;
        goto begin;
    }

错误2:下标溢出错误

for (vector<int>::size_type i = 0; i < ivec.size(); i = i + 2)
    {
        cout << ivec[i] + ivec[i + 1] << "\t";
        //每行输出的个数控制为5个。必须i+1,因为i初始值=0
        if (0 == (i + 1) % 5)
        {
            cout << endl;
        }
    }

i<ivec.size()这里出错,下标溢出,只有元素个数是偶数的时候不错,奇数就溢出了。修改后:

 1 #include <iostream>
 2 #include <vector>
 3 #include <string>
 4 using std::string;
 5 using std::vector;
 6 using std::cout;
 7 using std::cin;
 8 using std::endl;
 9
10 int main(void)
11 {
12     int in;
13     vector<int> ivec;
14     cout << "请输入一组整数:注意ctrl+z结束输入!" << endl;
15
16     while (cin >> in)
17     {
18         ivec.push_back(in);
19     }
20     //判断空输入否
21     if (0 == ivec.size())
22     {
23         cout << "输入为空,重新输入!" << endl;
24         system("pause");
25         return -1;
26     }
27     //求相邻的元素的和,关键算法!
28     //因为是相邻元素,故判断完毕,i+2之后再赋给i,跨度为2
29     //且不知道输入的元素是奇数,肯丢留最后一个元素不计算,是偶数则正好计算完毕!
30     //这里必须是i < size -1,否则下标溢出错误,偶数没问题,主要是奇数的话,如果到倒数第二个元素还+2,必然是溢出错误!
31     for (vector<int>::size_type i = 0; i < ivec.size() - 1; i = i + 2)
32     {
33         cout << ivec[i] + ivec[i + 1] << "\t";
34         //每行输出的个数控制为5个。必须i+1,因为i初始值=0
35         if (0 == (i + 1) % 5)
36         {
37             cout << endl;
38         }
39     }
40     //判断奇偶
41     if (0 != ivec.size() % 2)
42     {
43         cout << "元素个数为奇数,最后一个元素" << ivec[ivec.size() - 1] << "被忽略!" << endl;
44     }
45
46     cout << endl;
47
48     system("pause");
49     return 0;
50 }

问题8、读入一组整数到vector对象,使得头尾元素两两配对,计算和,并输出奇数元素个数的提示。

 1     vector<int> ivec;
 2     int in;
 3     cout << "输入一组正数,ctrl+z结束输入:" << endl;
 4
 5     while (cin >> in)
 6     {
 7         ivec.push_back(in);
 8     }
 9
10     if (0 == ivec.size())
11     {
12         cout << "空容器,必须输入元素!" << endl;
13         system("pause");
14         return -1;
15     }
16     //首末元素相加的处理,具有模版性质,也就是设计程序的通用性
17     //如果是奇数元素,那么中间的会留下,如果是偶数元素,没这个问题
18     vector<int>::size_type first = 0;
19     vector<int>::size_type last = 0;
20     vector<int>::size_type count = 0;//计数,控制打印输出
21
22     for (first = 0, last = ivec.size() - 1; first < last; first++, last--)
23     {
24         cout << ivec[first] + ivec[last] << "\t";
25         count++;
26         //控制打印输出每行3个数
27         if (0 == count % 3)
28         {
29             cout << endl;
30         }
31     }
32     //判断奇数还是偶数,给出提示
33     //灵活!使用简单的方式,如果是奇数的话,必然最后last和first重合
34     if (first == last)
35     {
36         cout << "中间的元素" << ivec[first] <<"留下了,因为元素个数是奇数!" << endl;
37     }
38
39     cout << endl;

问题9、读入一段文本到vector对象,把对象中每个元素里面的单词都转换为大写之后在输出,5个一行。

 1 #include <iostream>
 2 #include <vector>
 3 #include <string>
 4 #include <cctype>
 5 using std::string;
 6 using std::vector;
 7 using std::cout;
 8 using std::cin;
 9 using std::endl;
10
11 int main(void)
12 {
13     vector<string> svec;
14     string str;
15
16     while (cin >> str)
17     {
18         svec.push_back(str);
19     }
20
21     if (0 == svec.size())
22     {
23         return -1;
24     }
25
26     for (vector<string>::size_type i = 0; i != svec.size(); i++)
27     {
28         for (string::size_type j = 0; j != svec[i].size(); j++)
29         {
30             //如果是小写字母
31             if (islower(svec[i][j]))
32             {
33                 //转换为大写输出
34                 svec[i][j] = toupper(svec[i][j]);
35             }
36         }
37
38         cout << svec[i] << "\t";
39
40         if (0 == (i + 1) % 5)
41         {
42             cout << endl;
43         }
44     }
45
46     system("pause");
47     return 0;
48 }

问题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(10, 2);
    //把容器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 = 0;
    }

如果容器为空,则begin函数和end函数返回的迭代器相等,for循环测试失败,没问题!

问题12、两类只读迭代器类型

//类似普通的const常量,但是有区别。比如,如果仅仅想遍历容器的元素,对迭代器有const_iterator类型的迭代器,对它解引用,得到的是指向const对象的引用

 1     string str;
 2     vector<string> svec;
 3
 4     while (cin >> str)
 5     {
 6         svec.push_back(str);
 7     }
 8
 9     for (vector<string>::const_iterator iter = svec.begin(); iter != svec.end(); iter++)
10     {
11         //*iter = "dada";//error,const_iterator类型的迭代器,本身可以被改变,比如自增,但是迭代器指向的容器内的元素不能被修改!
12         //和普通的const常量有一些区别!有些类似指向常量的指针,指针本身可以变,但是指向的内容不能修改。
13     }

来对比const类型的迭代器,类似常指针,本身定义的时候必须初始化,本身不能被修改,但是指向的内容可以修改,如

1     vector<int> ivec(10);
2     //const类型的iter必须初始化
3     const vector<int>::iterator iter = ivec.begin();
4     //初始化之后不能被修改
5     //iter++;//error
6     //但是iter指向的内容可以被修改,对比,const_iterator类型的迭代器,类似指向常量的指针,迭代器本身能修改,指向的元素不能修改,,和他/她相反
7     *iter = 1;

注意区分两者,不要混淆。总结:

const 迭代器是迭代器常量

该迭代器本身的值不能修改,即该迭代器在定义时需要初始化,而且初始化之后,不能再指向其他元素。若需要指向固定元素的迭代器,则可以使用const 迭代器。但是它指向的元素的值可以被修改!

const_iterator 是一种迭代器类型

对这种类型的迭代器解引用会得到一个指向const 对象的引用,即通过这种迭代器访问到的对象是常量。该被指向的对象不能修改,因此,const_iterator 类型只能用于读取容器内的元素,不能修改元素的值。若只需遍历容器中的元素而无需修改它们,则可以使用const_iterator。但是迭代器本身能被修改。两者相反!

问题13、迭代器的算术操作

其他容器的迭代器类似。除了自增、自减之外,还有其他算术运算适用。

 1     vector<int> ivec(10);
 2     //const类型的iter必须初始化
 3     vector<int>::iterator iter = ivec.begin();
 4     //iter++;//ok
 5     //iter--;//ok
 6
 7     //iter + 5;//ok,对一个迭代器对象加一个整型值,使得iter指向新的元素第6个元素,注意不要越界
 8     //但是,加上或者减去的整型,最好是size_type类型的!
 9
10     //iter - 100;//error,越界,程序中断!
11
12     //iter + 10;//ok,加的时候,可以加到最后一个元素的下一位。
13
14     //还能求两个迭代器的距离
15     vector<int>::iterator iter1 = ivec.end();
16
17     cout << iter1 - iter << endl;//打印10
18
19     //注意,这里相减得到的值,可能是负数,也可以是正数
20     cout << iter - iter1 << endl;//打印-10
21     //这就说明,这个值的类型不再是容器的size_type类型,而是新的容器的类型,叫:
22     //differenec_type
23     vector<int>::difference_type i = iter - iter1;//ok,是带符号类型

注意,迭代器是没有相加操作的!比如:

    iter1 + iter1;//报错!

也就是说,要求得容器的中间元素,不能这样写:

vector<int>::iterator mid = (vi.begin() + vi.end())/2;

但是可以这样:

vector<int>::iterator mid = vi.begin() + vi.end()/2;

直接定位容器的中间元素,简单高效,不再需要一次次的去自增或者自减的遍历了。

 

辛苦的劳动,转载请注明出处,谢谢……

http://www.cnblogs.com/kubixuesheng/p/4138585.html

时间: 2024-08-31 12:27:02

把《c++ primer》读薄(3-2 标准库vector容器+迭代器初探)的相关文章

把《c++ primer》读薄(3-3 标准库bitset类型)

督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. //开头 #include <bitset> using std::bitset; 问题1.标准库bitset类型(模版) 需要处理二进制位的时候,可以使用c++标准库提供的bitset类型,它也是类模版,类似vectro容器,唯一不同的是,bitset类型需要说明长度,使用常量表达式给出的整型字面值或者已经初始化的cosnt对象. bitset<32> bit;//从0到31位算的,bit的32位每位初始化为

关于标准库vector的使用

问题描述 关于标准库vector的使用 在标准库容器vector中,这样是正确的: vectorvc; vector::iterator iter; iter=vc.begin(); iter++;//此时正确 但是下面这样是错误的: vectorvc; vc.begin()++//此时错误 为什么不能直接vc.begin()++呢? 解决方案 vc.begin()++被编译成 vc.begin() = vc.begin() + 1; 或者说 vector::iterator iter; ite

把《c++ primer》读薄(3-1 标准库string类型初探)

督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1:养成一个好习惯,在头文件中只定义确实需要的东西 using namespace std; //建议需要什么再using声明什么,最好不使用这个偷懒的写法 问题2:C++定义了一个内容丰富的抽象数据类型的标准库,最重要的两个标准库类型是string和vector 因为他们是c++基本内置类型基础上改进而来,故重要!前者支持变长字符串,后者可以保存一组指定类型的对象. 问题3:什么时候会调用默认的构造函数? 默认构造函数

c++-C++ string类 标准库 字符串

问题描述 C++ string类 标准库 字符串 C++标准库提供了一个功能强大的String类来实现字符串的操作,本系统创建一个功能类似"String类"的小型字符串类,以实现类似的字符处理功能求大神帮解决用C++ 解决方案 http://zhidao.baidu.com/link?url=75L1-WX-G-cxzSWytaJDkPNRF23cR08MvvIGyUSSG5ykmN9bh-A-0IFWPGBMKoMYkPJ0luXNApuvYXYT0TJQKa 解决方案二: <

我国首份IP地理信息标准库发布

中国第一个<互联网IP地理信息标准库>,于3月3日正式向行业推出.这是由中国广告协会互动网络分会面向全行业正式发布的第一个<互联网IP地理信息标准库>. 该库的准确率达到85%以上,对中国大陆地区全部IPv4地址达到了98%覆盖率.标准库的投入使用将极大提升了我国互联网广告地域定向投放的精度,有效避免因IP混乱造成不必要的浪费,这将进一步推动中国互联网产业向精确化.标准化方向的发展,为实现互联网广告的精准投放奠定了基础.这是我国互联网广告标准化的又一重大进步. 据了解,本次参与标准

C标准库参考指南(1)assert.h

1.1 assert.h 断言头文件用于调试. 宏: assert(); 外部引用: NDEBUG 1.1. assert 声明: void assert(intexpression); 断言头文件中的宏允许你将一些特殊信息写入到标准错误文件. 如果表达式的值为0(false),那么表达式.源文件名和行号都会被发送给标准错误输出,并调用abort函数.如果标识符NDEBUG ("no debug")由#define NDEBUG定义,那么断言头文件中的宏就什么都不做. 标准错误输出的格

Java SE 8:标准库增强

Lambda表达式是Java SE 8的核心功能,大部分的改进都围绕lambda表达式展开.(Jigsaw项目已经被推迟到Java SE 9.)关于lambda表达式的内容,已经在上一篇文章中进行了说明.这篇文章主要介绍Java SE 8中包含的其他Java标准库的增强. 并行排序 随着多核CPU的流行,Java平台的标准库实现也尽可能利用底层硬件平台的能力来提高性能.Java SE 7中引入了Fork/Join框架作为一个轻量级的并行任务执行引擎.Java SE 8把Fork/Join框架用到

为C++标准库容器写自己的内存分配程序

根据sgi 的STL源码的二级分配算法改写的内存池分配程序,只要稍微修改就可以实现共享内存方式管理,使用C++标准库容器中的map,set,multimap,multiset测试通过,vector测试通不过,原因是在内存回收的时候考虑的比较简单,vector每次分配内存个数不固定,回收也不固定,这样的话,程序还需要继续完善. 内存池管理程序源码如下: #ifndef MY_ALLOCATOR_H_#define MY_ALLOCATOR_H_#include "stdafx.h"#in

Python标准库之循环器 (itertools)

在循环对象和函数对象中,我们了解了循环器(iterator)的功能.循环器是对象的容器,包含有多个对象.通过调用循环器的next()方法 (__next__()方法,在Python 3.x中),循环器将依次返回一个对象.直到所有的对象遍历穷尽,循环器将举出StopIteration错误. 在for i in iterator结构中,循环器每次返回的对象将赋予给i,直到循环结束.使用iter()内置函数,我们可以将诸如表.字典等容器变为循环器.比如 for i in iter([2, 4, 5,