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(,);
vector<int> vInt4(); string str("volcanol");
vector<string> vStr1;
vector<string> vStr2(vStr1);
vector<string> vStr3(,str);
vector<string> vStr4(); return ;
}
这里需要注意的是, 对于存储string对象容器,可以用字符串字面值进行初始化, 如下所示:
vector<string> str(, "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(,);
vector<int> vInt4(); 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 ;
}
执行结果如下所示:
[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
the size of vector<int> vInt1 is:
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();
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 ;
}
执行结果如下:
[root@localhost cpp_src]# ./a.out
the size of vector<int> vInt1 is:
vector<int> vInt1 is empty
The size of vector<int> vInt1 is:
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=;i != ; i++)
vInt1.push_back(i); for(vector<int>::size_type i=; i != vInt1.size(); i++)
cout<<vInt1[i]<<endl; return ;
}
执行结果如下:
[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
要点: 容器的下标是从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(); for(vector<int>::size_type i = ; i != vInt1.size(); i++)
vInt1[i]=i; for(vector<int>::size_type i=; i != vInt1.size(); i++)
cout<<vInt1[i]<<endl; cout<<"the size of vInt1 is: "<<vInt1.size()<<endl;
return ;
}
程序执行的结果是:
[root@localhost cpp_src]# ./a.out the size of vInt1 is:
要点: 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(); 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 ;
}
程序执行结果如下:
[root@localhost cpp_src]# ./a.out
abd
abd
再次执行结果如下:
[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(); 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 ;
}
程序执行结果为:
[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();
vector<string>::iterator iter=vStr.begin(); for(vector<string>::size_type i=; i !=vStr.size();i++)
{
cin>>vStr[i];
} while(iter != vStr.end())
{
cout<<*iter<<endl;
++iter;
} return ;
}
执行结果如如下所示:
[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:
#include <iostream>
#include <string>
#include <vector> using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector; int main()
{
const vector<int> vInt(); //ok,vInt[i]=0;
cout<<vInt[]<<endl; vector<int>::iterator iter=vInt.begin();
*iter = ;
cout<<vInt[]<<endl; return ;
}
编译的时候提示:
[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 错误:请求从 ‘__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();
vector<int>::iterator iter=vInt.begin(); for(;iter != vInt.end(); ++iter)
cin>>*iter; iter=vInt.begin();
iter = iter + vInt.size()/;
cout<<*iter<<endl; iter = iter - ;
cout<<*iter<<endl; return ;
}
执行结果如下所示:
[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
今天关于vector和迭代器就描述到这里,后续内容,未完待续..........................
C++_系列自学课程_第_5_课_vector容器_《C++ Primer 第四版》的更多相关文章
- C++_系列自学课程_第_7_课_数组_《C++ Primer 第四版》
说到数组,大家应该都很熟悉,在C.Pascal.Java等语言中,都有数组的概念.在C++中也提供了对数组的支持.数组简单来说就是一堆相同 数据类型对象的集合. 这里要把握住两个要点: 相同的数据类型 ...
- C++_系列自学课程_第_6_课_bitset集_《C++ Primer 第四版》
在C语言中要对一个整数的某一个位进行操作需要用到很多的技巧.这种情况在C++里面通过标准库提供的一个抽象数据类型 bitset得到了改善. 一.标准库bitset类型 1.bitset的作用 bits ...
- C++_系列自学课程_第_3_课_变量和基本类型_《C++ Primer 第四版》
最近复习C++相关内容,决定在这里记录自己复习的过程. 以前写过部分文字,但是没有坚持连续写,因此学完后 基本又忘光啦,主要是没有实践,这一次决定自学完后,在这里在复习一遍增强自己的记忆和理解程度. ...
- C++_系列自学课程_第_12_课_结构体
#include <iostream> #include <string> using namespace std; struct CDAccount { double bal ...
- C++_系列自学课程_第_12_课_语句_《C++ Primer 第四版》
前面的文章说完了表达式和类型转换的部分内容,在我参考的书里面,接下来讨论的是各种语句,包括:顺序语句.声明语句.复合语句(块语句).语句作用域 .if语句.while语句.for语句.do...whi ...
- C++_系列自学课程_第_11_课_类型转换_《C++ Primer 第四版》
上次说了关于表达式的一些内容,说到还有一些关于数据类型转换的内容,今天我们接着八一八C++中的数据类型转换. 一.隐式类型转换 在表达式中,有些操作符可以对多种类型的操作数进行操作, 例如 + 操作符 ...
- C++_系列自学课程_第_10_课_表达式_《C++ Primer 第四版》
程序设计语言中大部分程序都在进行表达式的求值操作, 例如求两个数的和,求一个表达式的逻辑结果,或者通过输入输出表达式语句进行输入和输出. 这里我们对表达式进行讨论. 一.表达式 1.表达式 表达式由一 ...
- C++_系列自学课程_第_9_课_C语言风格字符串_《C++ Primer 第四版》
前面说了写关于数组和指针的内容,这次在这里讨论一下字符串,讨论一下C语言风格的字符串. 在C语言里面我们利用字符数组来对字符串进行处理, 在C++里面我们前面说过一种类类型string可以对字符串进行 ...
- C++_系列自学课程_第_8_课_指针和引用_《C++ Primer 第四版》
C语言最富有迷幻色彩的部分当属指针部分,无论是指针的定义还是指针的意义都可算是C语言中最复杂的内容.指针不但提供给了程序员直接操作硬件部分的操作接口,还提供给了程序员更多灵活的用法.C++继承这一高效 ...
随机推荐
- 如何让用户只能访问特定的数据库(MSSQL)
背景 客户的SQL Server实例上有多个厂商的数据库,每个数据库由各自的进行厂进行商维护, 为了限定不同厂商的维护人员只能访问自己的数据库,现需要给各个厂商限定权限,让他们登录SQL Server ...
- [每日电路图] 10、两种MOS管的典型开关电路
下图是两种MOS管的典型应用:其中第一种NMOS管为高电平导通,低电平截断,Drain端接后面电路的接地端:第二种为PMOS管典型开关电路,为高电平断开,低电平导通,Drain端接后面电路的VCC端. ...
- [nRF51822] 12、基础实验代码解析大全 · 实验19 - PWM
一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指 ...
- Atitit 代码复用的理解attilax总结
Atitit 代码复用的理解attilax总结 1.1. 继承1 1.1.1. 模式1:原型继承1 1.1.2. 模式2:复制所有属性进行继承 拷贝继承1 1.1.3. 模式3:混合(mix-in)1 ...
- C#设计模式-代理模式
在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代理对象代替 ...
- Ajax概要:
Ajax概要: Ajax不是个全新的技术,它是多种技术合并在一起产生的,包括XHTML,CSS,JavaScript,XmlHttpRequest,XML,JSON,DOM等 优点:(这也解释了为何我 ...
- 【Win10 应用开发】集成语音命令
记得老周以前在写WP8应用开发的文章时,曾经写过语音命令集成的文章,后来8.1的时候“小娜”问世,但考虑到其变化不大,故老周没有补写相应的文章. 今天,老周打算补一下Win 10通用应用开发中,有关语 ...
- VB.NET 创建文件以及文件的读写(创建随机数)
创建文件 Dim strFile As String = String.Format("C:\ErrorLog.txt", DateTime.Today.ToString(&quo ...
- 深入学习jQuery元素尺寸和位置操作
× 目录 [1]尺寸设置 [2]位置设置 前面的话 对于javascript来说,元素尺寸有scroll.offset.client三大属性,以及一个强大的getBoundingClientRect( ...
- 修改USB固件库的Customer_HID例程
我用的是神州三号开发板子,板子的USB模块原理图为: 配置端口G的11号引脚为usb的使能引脚,按理来说应该是开漏输出的(看了很多的修改代码都是这个模式),不过就是不能使能usb,只能配置成推挽的才行 ...