C++_系列自学课程_第_5_课_vector容器_《C++ Primer 第四版》

   再一次遇到 vector 这个单词; 每一次见到这个单词都感觉这个单词非常的 "高大上"; 数字遇到vector马上就可以360度旋转;

当 "电" 遇到vector,马上让交流可以变得和直流一样进行控制(德国电气工程师的矢量控制理论,目前在工控界对电机控制应用

非常广泛,是变频器控制的基础理论,可惜的是中国目前没有这方面的真正的专家, 就是IT行业中的TI公司的TMS320LF24xx系列

DSP做的事,中国的基础理论的研究真的是落后于西方发达国家很多年),而在C++中遇到这个单词,同样是高大上的感觉,马上

让我想起C语言中最复杂的部分指针; 别说指针的操作你无所谓, 就算你是C++、C#、Java的粉丝;没有指针,个人感觉就像没

有金箍棒的猴子, 幻刺模糊直接让你子弹到处乱飞。

  不瞎扯啦,开始今天的内容。

一、vector

1、作用

  vector,从翻译对应的词语: 容器, 可以知道vector是用来装东西的罐子、坛子之类的东西;联系实际,坛坛罐罐可以用来装各

种不同的东西, 可以用来盛酒, 也可以用来放酸菜(南北朝鲜的酸菜就算啦),当然你如果是土豪的话,也可以用来放钢镚;如果你

贪官的话,也可以用来藏你给情妇写的保证书什么的。

  从C++的角度来看,vector可以用来存放不同类型的对象,例如可以把int类型的对象存放到容器里面,同样也可以将char类型的

象存储到vector中,也可以将string对象存储到vector里面。

2、容器的定义

  vector类型是C++标准库提供的一种类型,如果要在程序中引用这种类型,当然需要“报备”, 如下所示:

#include <vector>  //引用相关的头文件
using std::vector;   //方便的使用std::命名空间/名字空间中的名字vector

  报备完成后就可以名正言顺的经营啦;要使用 vector 必须指明容器里面存放的是什么类型的对象,就像在坛坛罐罐上面贴上标签

告诉别人里面存放的是什么;通过在 vector后面加上 <> 然后将对象类型写入到 <> 就可以啦, 如下所示:

vector<int> vInt;

  如上就定义了一个容器对象, 并且这个容器是用来存储int类型的对象的。

   要点:尖括号里面指出的是容器中存放的是什么类型的对象; 同时要注意的是 vector<int>是一种数据类型,

   vector可以存储的对象不仅可以是内置类型,还可以是用户的自定义类型,例如string类。

vector<string>  vString;

  同样 vector<string> 也是一种新的数据类型, 这种数据类型的对象用来存储string对象。

 

3、容器的初始化

  标准库提供的vector类型有多种初始化方式。

  A:  vector<T>   v1;   //容器对象v1保存类型为T的对象, 容器类型的默认构造函数初始化对象v1为空

   这样定义的话,就是定义了一个容器,里面没有任何东西,就相当于一个空的坛子、罐子。

  B:  vector<T>   v2(v1);  //容器对象v2保存类型为T的对象, 对象v2初始化为与v1一样的容器

  这样定义的话,就表示开始的时候有一个罐子v1,然后又造了一个罐子v2,v2罐子是按照罐子v1为原型造的,如果v1里面有东西

 那么v2罐子里面的东西与v1罐子一样; 如果v1罐子里面没有东西,那么v2罐子里面也没有东西。

  C:  vector<T>   v3(n, i);  //容器v3保存类型为T的对象, 对象初始化为保存n个类型为T、值为 i 的对象元素

  这样定义的话,就表示下了一个明确订单, 就是告诉你,你给我造一个罐子v3, 里面放多少封(n)给情妇的保证书(i)。 

  D: vector<T>   v4(n);    //容器v4保存类型为T的对象, 容器对象v4中初始化里面可以存放个数为n的T对象。 

  这样的定义的话,就是下了一个明确的订单, 你给某个贪官造个罐子v4, 这个罐子可以放多少(n)封给情妇的保证书, 但是你不能

放保证书,放保证书的事情让贪官自己以后放。

Exp:

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt1;
    vector<int> vInt2(vInt1);
    vector<int> vInt3(3,4);
    vector<int> vInt4(5);

    string str("volcanol");
    vector<string> vStr1;
    vector<string> vStr2(vStr1);
    vector<string> vStr3(4,str);
    vector<string> vStr4(5);

    return 0;
}

  这里需要注意的是, 对于存储string对象容器,可以用字符串字面值进行初始化, 如下所示:

vector<string>  str(4, "volcanol");

 

4、容器元素的值的初始化

  一定要分辨清楚容器的初始化和容器元素的值的初始化两个概念, 容器的初始化可以包括两个方面:容器本身的初始化

以及容器元素的值的初始化。

  这里就细细的说一下这两个的不同之处。

  A:   vector<int>  vInt;     这里对容器对象vInt进行初始化,初始化后知道对象vInt是一个空的对象,里面暂时不能存

放任何int对象, 就是说这个坛子是一个实心的坛子,不能往里面塞东西。

  B:vector<int>  vInt1(2, 10);  这里不但对容器对象vInt1进行了初始化,同时还对容器对象里面的元素进行了初始化,

就是说容器里面存放了两个int类型的对象,且两个int对象的值均为10.  用坛坛罐罐来比喻就是罐子里面放了2个(初始化的是容器)

10元的钢镚(初始化的是容器里面的钢镚的大小)。

  C:  vector<int> vInt2(10);  这里对容器vInt2进行了初始化,而且在容器里面目前可以放10个int对象,而且这些int对象

的值为0。  用坛坛罐罐来比喻的话就是罐子里面放了10个面值为0元的钢镚。

 

  要点:  

  如果容器存放的对象类型为内置类型,且没有提供容器元素的初始化值,那么就会初始化容器的元素值为0.

例如:

    vector<char>  vCh(1);   那么容器vCh里面就有一个元素之为'\0'的的char对象。

  如果容器存放的对象是类类型,且没有提供容器元素的初始化值,那么容器元素就会用类类型默认构造函数对容器的

元素进行初始化。

例如:

    vector<string>  vStr(2);  那么容器vStr里面就存放了两个值为空""字符串的string对象。

  如果容器存放的对象是类类型,同时类类型没有默认构造函数,且没有提供元素的初始化值, 那么标准库同样会产生

一个带初始值的对象,这个对象的每个成员都进行了初始化。(这个地方说的有点不明不白,我也是每天搞明白,有哪位大侠了

解的话,请不吝赐教)。

 

5、vector提供的操作

  vector类提供了类似于string类的操作。

    v.empty();  检测对象v是否为空

    v.size();  返回对象v中的元素的个数

    v.push_back(t);  往对象v中插入一个值为(t)新的元素, 这个就是动态的增加容器对象的容量,

    v[n];  访问容器中的元素, 相当于string对象的下标操作,而且容器对象下标操作符返回的是一个左值,可以进行写入操作。

    v1 = v2;  容器的赋值操作, 就是将容器对象v1的元素替换为v2中元素的副本,(这需要注意容器的大小)

    v1 == v2; 如果容器v1与v2相等,那么就返回true, 这里包括容器本身以及容器中的元素,这个必须要注意。  

     >, >=, != ,  <, <= 这些操作保持原本的意义。

Exp1:size() 和 empty()操作

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt1;
    vector<int> vInt2(vInt1);
    vector<int> vInt3(3,4);
    vector<int> vInt4(5);

    cout<<"the size of vector<int> vInt1 is:"<<vInt1.size()<<endl;
    if(vInt1.empty())
        cout<<"vector<int> vInt1 is empty"<<endl;
    else
        cout<<"vector<int vInt1> is not empty"<<endl;

    return 0;
}

执行结果如下所示:

[root@localhost cpp_src]# g++ test.cpp 
[root@localhost cpp_src]# ./a.out 
the size of vector<int> vInt1 is:0
vector<int> vInt1 is empty

 

Exp: push_back()操作

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt1;

    cout<<"the size of vector<int> vInt1 is:"<<vInt1.size()<<endl;
    if(vInt1.empty())
        cout<<"vector<int> vInt1 is empty"<<endl;
    else
        cout<<"vector<int vInt1> is not empty"<<endl;
    
    vInt1.push_back(1);
    cout<<"The size of vector<int> vInt1 is:"<<vInt1.size()<<endl;
    if(vInt1.empty())
        cout<<"vector<int> vInt1 is empty"<<endl;
    else
        cout<<"vector<int> vInt1 is not empty"<<endl;

    return 0;
}

执行结果如下:

[root@localhost cpp_src]# ./a.out 
the size of vector<int> vInt1 is:0
vector<int> vInt1 is empty
The size of vector<int> vInt1 is:1
vector<int> vInt1 is not empty

  可以发现执行 vInt1.push_back(1); 以后vInt的大小变成了1,而且不再是空的容器。

 

Exp:  容器的下标操作

  容器的下标操作返回的是容器里面的元素,这个要特别的注意。

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt1;
    
    for(vector<int>::size_type i=0;i != 10; i++)
        vInt1.push_back(i);

    for(vector<int>::size_type i=0; i != vInt1.size(); i++)
        cout<<vInt1[i]<<endl;

    return 0;
}

执行结果如下:

[root@localhost cpp_src]# g++ test.cpp 
[root@localhost cpp_src]# ./a.out 
0
1
2
3
4
5
6
7
8
9

  要点:  容器的下标是从0开始计算的,   其最大值为  v.size( ) - 1;  所以上面的第二个循环可以正常执行,

当i=9的时候, i  != vInt1.size( ),因此可以进行循环;

      而当i=10 的时候,i == vInt1.size(),  for循环的条件不成立,因此跳出循环,不会产生下标越界。

  vector<int>::size_type 类型为标准库为vector类型定义的表示vector大小的类型, 在使用这个数据类型的

时候对象的类型 int 不能少,否则就会出错, 这个地方经常会被遗忘。  

 

Exp: 下标操作的赋值

  

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt1(10);

    for(vector<int>::size_type i = 0; i != vInt1.size(); i++)
        vInt1[i]=i;
    
    for(vector<int>::size_type i=0; i != vInt1.size(); i++)
        cout<<vInt1[i]<<endl;

    cout<<"the size of vInt1 is: "<<vInt1.size()<<endl;
    return 0;
}

程序执行的结果是:

[root@localhost cpp_src]# ./a.out 
0
1
2
3
4
5
6
7
8
9
the size of vInt1 is: 10

  要点:  vector下标操作返回的是容器里面存放的对象, 是一个左值。 

 

二、 迭代器  iterator

  C++标注库为访问vector提供了另外的一种机制,这个机制就是迭代器。

1、作用

  迭代器的作用就是用来访问容器中的元素。

2、迭代器的定义

  每一种类型的vector类型都定义了自己的迭代器类型,定义一个迭代器的语法如下所示:

    vector<T>::iterator  tIter;

  这样就定义了一个用于容器类型为 vector<T>的迭代器对象   tIter; tIter的数据类型是 vector<T>::iterator,

在C++中可以通过迭代器来访问容器里面的元素。

  默认定义通过上面的定义 tIter 将指向容器中的第一个元素,如果容器不为空的话。

 

  迭代器的概念与C语言中的指针非常类似,都有一个指向的概念,但是要严格区分两者的区别。

 

3、迭代器的begin和end操作

  每一种标准库定义的容器都定义了两个操作:  begin() 和 end() 操作。 begin()操作返回的迭代器指向的容器中的

第一个元素, 而end()操作则返回迭代器指向的容器中最后一个元素后面的一个位置, 通常称为 超出末端迭代器, 表明指向了一个

不存在的元素; 如果容器为空,那么迭代器的begin() 和end()操作将指向同一个位置---超出末端迭代器的位置。

 

4、迭代器的解引用和自增操作

  通过迭代器的解引用操作可以和下标操作符一样操作容器中的元素。

  例如:

      vector<int> vInt(3);

      vector<int>::iterator iter=vInt.begin( );   //迭代器指向 vInt容器中的第一个元素,vInt容器中一共有3个元素。

      *iter = 10;  ====》相当于  vInt[0]  = 10;

  通过移动迭代器的指向,可以访问容器中不同的元素。可以通过自增运算符来移动迭代器的指向。  

      vector<int> vInt(3);

      vector<int>::iterator iter=vInt.begin( );   //迭代器指向 vInt容器中的第一个元素,vInt容器中一共有3个元素。

      *iter = 10;  ====》相当于  vInt[0]  = 10;

         ++ iter ;

      *iter = 5;  =====>>相当于 vInt[1] = 5;

      ++ iter ;

      *iter = 1 ;  =====>>相当于  vInt[2] = 1;

       iter = vInt.begin();    =====》相当于将迭代器再次指向容器中的第一个元素,

       * iter = 3 ;  ======》 相当于vInt[0] = 3;

Exp:

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<string> vStr(3);

    vector<string>::iterator iter=vStr.begin();

    while(iter != vStr.end())
    {
        cin>>*iter;
        ++iter;
    }

    iter=vStr.begin();
    while(iter != vStr.end())
    {
        cout<<*iter<<endl;
        ++iter;
    }

    return 0;
}

程序执行结果如下:

[root@localhost cpp_src]# ./a.out 
abd 121 456
abd
121
456

再次执行结果如下:

[root@localhost cpp_src]# ./a.out 
volcanol hello world  hi
volcanol
hello
world

 

5、迭代器的比较操作

  可以测试两个迭代器是否指向容器中的同一个元素,测试用的操作符为:== 和 != 。

exp:

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<string> vStr(3);

    vector<string>::iterator iter1=vStr.begin();
    vector<string>::iterator iter2=vStr.end();

    if(iter1 == iter2)
        cout<<"iter1 and iter2 point to the same element"<<endl;
    else
        cout<<"iter1 and iter2 don't point to the same element "<<endl;

    ++iter1;
    ++iter1;
    ++iter1;

    cout<<"after iter1 moved:";
    if(iter1 != iter2)
        cout<<"iter1 and iter2 don't point to the same element"<<endl;
    else
        cout<<"iter1 and iter2 point to the same element"<<endl;


    return 0;
}

程序执行结果为:

[root@localhost cpp_src]# ./a.out 
iter1 and iter2 don't point to the same element 
after iter1 moved:iter1 and iter2 point to the same element

 

6、利用迭代器循环遍历容器中的元素

  前面我们利用下标操作符和 v.size()操作遍历了整个容器的元素,现在我们利用迭代器遍历容器。

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<string> vStr(3);
    vector<string>::iterator iter=vStr.begin();


    for(vector<string>::size_type i=0; i !=vStr.size();i++)
    {
        cin>>vStr[i];
    }

    while(iter != vStr.end())
    {
        cout<<*iter<<endl;
        ++iter;
    }


    return 0;
}

执行结果如如下所示:

[root@localhost cpp_src]# g++ test.cpp 
[root@localhost cpp_src]# ./a.out 
kiki gaida volcanol hi world
kiki
gaida
volcanol

 

7、const_iterator  和 const vector<T>::iterator 

     看到这个题头,就会想起C语言中指针和const的结合。

  const_iterator的迭代器不能用来改变容器中元素的值,而 const vector<T>::iteraor则不能改变指向。

例如:

  vector<int> vInt(3, 10);

  vector<int>::const_iterator iter1= vInt.begin();

  *iter1 = 5 ;  //错误,不能通过const_iterator迭代器改变容器中元素的值。

  ++iter;  //正确,可以改变迭代器的指向。

 

而下面的情形是:

  vector<string>  vStr(3,"hi");

  const vector<string>::iterator  iter2=vStr.begin();

  *iter2 = "volcanol";  //正确,可以通过迭代器的解引用操作符来操作容器中的元素。

  iter2 =vStr.end() ;  //错误,  const 迭代器的指向不能改变。

 

而下面的情形是:

  const vector<int>  vInt(5,1);

     vector<int>::iterator  iter=vInt.begin();    //错误,不能这样定义

exp:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 
 5 using std::cin;
 6 using std::cout;
 7 using std::endl;
 8 using std::string;
 9 using std::vector;
10 
11 int main()
12 {
13     const vector<int> vInt(3); //ok,vInt[i]=0;
14     cout<<vInt[0]<<endl;
15 
16     vector<int>::iterator iter=vInt.begin();
17     *iter = 10; 
18     cout<<vInt[0]<<endl;
19 
20     return 0;
21 }

编译的时候提示:

[root@localhost cpp_src]# g++ test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:16: 错误:请求从 ‘__gnu_cxx::__normal_iterator<const int*, std::vector<int, std::allocator<int> > >’ 转换到非标量类型 ‘__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >’

  提示16行错误。

 

而下面的情形是:

  vector<int>  vInt(10,1);

  const vector<int>::const_iterator  iter=vInt.begin();  //正确,这样可以定义

这个地方需要注意, 第一个const指出的是 iter的指向不能改变, 第二个const_iterator 指出不能通过 *iter 改变容器中

元素的值。

 

要点:

  const修饰时迭代器和类型和容器内元素的类型要一致,如果元素不可改变,那么就不能定义可以改变容器元素的迭代器来

访问容器中的元素。

 

8、迭代器支持算术运算

  可以给迭代器进行 +n 和-n 的操作,就和C语言中的指针的加法、减法运算类似。

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    vector<int> vInt(10);
    vector<int>::iterator iter=vInt.begin();

    for(;iter != vInt.end(); ++iter)
        cin>>*iter;

    iter=vInt.begin();
    iter = iter + vInt.size()/2;
    cout<<*iter<<endl;

    iter = iter - 2;
    cout<<*iter<<endl;


    return 0;
}

执行结果如下所示:

[root@localhost cpp_src]# g++ test.cpp 
[root@localhost cpp_src]# ./a.out  
1 2 3 4 5 6 7 8 9 10 11
6
4

 

 

  今天关于vector和迭代器就描述到这里,后续内容,未完待续..........................

转载于:https://www.cnblogs.com/volcanol/p/4002618.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《C Primer》第5版是一本C语言编程的经典教材,编写了三位C语言领域的专家,清晰地、基础地、全面地介绍了C语言的基础知识和高级编程技巧。 本书的章节安排很合理,首先介绍了C语言的概述和基础知识,然后深入讲解了程序结构、数据类型、函数、数组、指针、字符串、结构体等内容,最后详细探究了C语言程序调试、内存管理、文件处理等高级应用技巧。配合书中的大量实例和习题,读者可以逐步壮大自己C语言的能力和经验。 更值得一提的是,本书既可供中高级程序员学习参考,又可作为初学者的首选入门教材,对于不同程度的读者,本书都能起到很好的指导作用。同时,书中涉及了一些现代化的编程技术和工具,例如C++语言的特性、程序设计的实践方法、线程和多线程编程等,这对于对C语言开发有更高追求的读者也是非常有益的。 总的来说,《C Primer》第5版是一本基础全面、引人入胜、适合不同学习者的优秀C语言教材,彰显了编写者的深厚学术功底和丰富的实践经验。相信任何希望深入理解C语言和提高编程能力的人都应该读一读它。 ### 回答2: 《C Primer》是一本经典的C语言教材,已经出到第5版,本文将从以下几个方面对这本书进行回答。 首先,本书的内容十分系统和全面,从C语言基础概念的介绍开始,一步步地引导读者逐渐掌握C语言的语法和编程技巧,涵盖了C语言的各个方面,包括数据类型、运算符、数组、指针、结构体、函数、文件操作、动态内存管理等。 其次,这本书深入浅出,充满了很多实际的例子和练习题,让读者能够更好地理解和掌握C语言的知识。此外,书中也有很多注意事项和技巧提示,帮助读者避免一些常见的错误和陷阱。 再次,本书具有很强的实用性,所有涉及的知识都有大量的编程实例,读者可以通过实际动手编写程序来巩固知识和提高技能。此外,书中也有很多高级的话题和扩展阅读材料,对于想深入了解C语言的读者来说,这些内容也是值得一看的。 最后,需要注意的是,由于C语言的发展和变化,本书所涉及的一些细节和用法可能已经有所过时,所以在实际编程中还需要结合具体的实践和查阅相关文献来进行处理。不过无论如何,《C Primer》仍是一本不可多得的优秀C语言教材,对于初学者和中级开发者来说都是一本不可多得的参考书籍。 ### 回答3: 《C Primer》是 C 语言的入门经典教材之一,在第五版中进行了全面的更新和修订,以反映当前的 C 语言标准和最佳编程实践。作为一本经典教材,它适用于既没有 C 编程经验也没有计算机科学背景的初学者,同时也适合作为专业程序员的参考工具书。本书旨在帮助初学者全面掌握 C 语言的基本概念、核心语法、程序化思考和最佳编程实践,以及深入理解计算机的原理和机制。 本书内容分为两部分:基础和高级篇。基础篇从 C 语言的语法和控制流开始,介绍了各种基本类型、数组、指针、结构体和函数等,同时也介绍了 C 语言的输入输出和文件处理,以及如何进行调试和错误处理。高级篇则更深入一些,包括了动态内存管理、多线程编程、位运算、预编译器、C11 及 C17 标准等方面的内容。在这一部分中,读者可以学会如何构建更加复杂的程序和模块,同时了解如何使用 C 语言的高级特性。 本书有以下几个特点: 1、全面而深入的讲解:本书对 C 语言的各个方面进行了详细而全面的讲解,同时也深入介绍了计算机的底层机制和编程实践。 2、严谨和实用的教学方法:本书的教学方法既严谨又实用,通过大量的代码示例和练习,读者可以快速掌握 C 语言的核心概念和编程技巧。 3、与时俱进的内容:本书对 C 语言的最新标准和编程实践进行了收录,确保读者学到的是最新、最优秀的编程实践。 总之,《C Primer》是一本非常优秀的 C 语言教材,无论是初学者还是专业程序员都可以从中受益。如果你希望快速掌握 C 语言并用它来进行高效的编程工作,那么这本书是你不可或缺的工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值