vector的详细说明
发布时间:2025-05-15 18:43:18 发布人:远客网络
一、vector的详细说明
vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。
和 string对象一样,标准库将负责管理与存储元素相关的内存。我们把 vector称为容器,是因为它可以包含其他对象,能够存放任意类型的动态数组,增加和压缩数据。一个容器中的所有对象都必须是同一种类型的。
vector是一个类模板(class template)。使用模板可以编写一个类定义或函数定义,而用于多个不同的数据类型。因此,我们可以定义保存 string对象的 vector,或保存 int值的 vector,又或是保存自定义的类类型对象(如Sales_items对象)的 vector。vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。
为了可以使用vector,必须在你的头文件中包含下面的代码:
vector属于std命名域的,因此需要通过命名限定,如下完成你的代码:
建议在代码量不大,并且使用的命名空间不多的情况下,使用全局的命名域方式:using namespace std;
c.assign(beg,end) c.assign(n,elem)
将(beg; end)区间中的数据赋值给c。将n个elem的拷贝赋值给c。
传回索引idx所指的数据,如果idx越界,抛出out_of_range。
传回最后一个数据,不检查这个数据是否存在。
传回迭代器中的第一个数据地址。
c.end()//指向迭代器中末端元素的下一个,指向一个不存在元素。
c.erase(pos)//删除pos位置的数据,传回下一个数据的位置。
删除[beg,end)区间的数据,传回下一个数据的位置。
c.insert(c.begin()+pos,elem)//在pos位置插入一个elem拷贝,传回新数据位置
c.insert(c.begin()+pos,n,elem)//在pos位置插入n个elem数据,无返回值
c.insert(c.begin()+pos,beg,end)//在pos位置插入在[beg,end)区间的数据。无返回值
传回一个逆向队列的第一个数据。
传回一个逆向队列的最后一个数据的下一个位置。
vector<Elem>//创建一个空的vector
vector<Elem> c1(c2)//复制一个vector
vector<Elem> c(n)//创建一个vector,含有n个数据,数据均已缺省构造产生
vector<Elem> c(n,elem)//创建一个含有n个elem拷贝的vector
vector<Elem> c(beg,end)//创建一个以(beg;end)为区间的vector
c.~ vector<Elem>()//销毁所有数据,释放内存
返回容器中指定位置的一个引用。
vector容器提供了多种创建方法,下面介绍几种常用的。
创建一个Widget类型的空的vector对象:
vector<Widget> vWidgets;
创建一个包含500个Widget类型数据的vector:
vector<Widget> vWidgets(500);
创建一个包含500个Widget类型数据的vector,并且都初始化为0:
vector<Widget> vWidgets(500,Widget(0));
vector<Widget> vWidgetsFromAnother(vWidgets);
vector添加数据的缺省方法是push_back()。push_back()函数表示将数据添加到vector的尾部,并按需要来分配内存。例如:向vector<Widget>;中添加10个数据,需要如下编写代码:
vWidgets.push_back(Widget(i));
vector里面的数据是动态分配的,使用push_back()的一系列分配空间常常决定于文件或一些数据源。如果想知道vector是否为空,可以使用empty(),空返回true,否则返回false。获取vector的大小,可以使用size()。例如,如果想获取一个vector v的大小,但不知道它是否为空,或者已经包含了数据,如果为空时想设置为-1,你可以使用下面的代码实现:
int nSize= v.empty()?-1: static_cast<int>(v.size());
operator[]主要是为了与C语言进行兼容。它可以像C语言数组一样操作。但at()是我们的首选,因为at()进行了边界检查,如果访问超过了vector的范围,将抛出一个例外。由于operator[]容易造成一些错误,所以我们很少用它.
vector能够非常容易地添加数据,也能很方便地取出数据,同样vector提供了erase(),pop_back(),clear()来删除数据,当删除数据时,应该知道要删除尾部的数据,或者是删除所有数据,还是个别的数据。
remove()算法如果要使用remove,需要在头文件中包含如下代码:
1、 iterator _First:指向第一个数据的迭代指针。
2、 iterator _Last:指向最后一个数据的迭代指针。
3、 predicate _Pred:一个可以对迭代操作的条件函数。
条件函数是一个按照用户定义的条件返回是或否的结果,是最基本的函数指针,或是一个函数对象。这个函数对象需要支持所有的函数调用操作,重载operator()()操作。remove是通过unary_function继承下来的,允许传递数据作为条件。
例如,假如想从一个vector<CString>;中删除匹配的数据,如果字串中包含了一个值,从这个值开始,从这个值结束。首先应该建立一个数据结构来包含这些数据,类似代码如下:
class FindMatchingString: public std::unary_function<CString,bool>{
FindMatchingString(const LPFINDSTR lpFS):
bool operator()(CString& szStringToCompare) const{
retVal=(szStringToCompare== m_lpFDD->szMatchStr);
retVal=(szStringToCompare.Left(m_lpFDD->szMatchStr.GetLength())
== m_lpFDD->szWindowTitle);
retVal=(szStringToCompare.Right(m_lpFDD->szMatchStr.GetLength())
retVal=(szStringToCompare.Find(m_lpFDD->szMatchStr)!=-1);
通过这个操作你可以从vector中有效地删除数据:
vs.erase(std::remove_if(vs.begin(),vs.end(),FindMatchingString(&fs)),vs.end());
Remove(),remove等所有的移出操作都是建立在一个迭代范围上的,不能操作容器中的数据。所以在使用remove,实际上操作的时容器里数据的上面的。
看到remove实际上是根据条件对迭代地址进行了修改,在数据的后面存在一些残余的数据,那些需要删除的数据。剩下的数据的位置可能不是原来的数据,但他们是不知道的。
调用erase()来删除那些残余的数据。注意上面例子中通过erase()删除remove的结果和vs.enc()范围的数据。
no matching function for call to‘std::vector,一般由定义的类型与存入的类型不匹配引起。
二、c语言中的头文件
c语言中的头文件:#include<stdio.h>。
1.头文件可以定义所用的函数列表,方便查阅你可以调用的函数。
2.头文件可以定义很多宏定义,就是一些全局静态变量的定义,在这样的情况下,只要修改头文件的内容,程序就可以做相应的修改,不用亲自跑到繁琐的代码内去搜索。
3.头文件只是声明,不占内存空间,要知道其执行过程,要看你头文件所申明的函数是在哪个.c文件里定义的,才知道。
C语言是一门通用计算机编程语言,应用广泛。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。
三、用memcpy函数拷贝vector
定义了两个vector,vector里面存的是类,是否可以直接使用memcpy去复制vector?这个问题涉及C库函数strcpy、memcpy和memmove。让我们先了解这三种函数,然后再解答这个问题。
strcpy, memcpy, memmove都是C库函数,它们的原型如下:
strcpy不需要传入复制的字节数,而memcpy和memmove需要,这是因为memcpy和memmove需要明确知道要复制的字节数。
我们先考虑vector存放内置类型的情况。不同于数组,vector对象在使用时不会转换成指针,因此,需要使用取地址符&来得到vector对象的地址。
执行上述代码后,我们发现test1中元素的地址可以正常输出。
然而,当程序退出时,引发"读取位置 0xFFFFFFFFFFFFFFFF时发生访问冲突"的异常,这是为什么呢?我们可以发现,test1中元素的地址和test中的是相同的!那么,我们可以做出如下猜测:
当程序退出时,后声明的test1先被释放。轮到test调用其析构函数释放内存时,其中的元素在test1释放时就已经被释放,再尝试访问这些元素进行内存释放会引发“访问冲突”的异常。
我们先给test1分配空间,并默认初始化:
此时程序正常运行并正常退出。我们用&test[0],&test1[0]真正地取到了test, test1的首元素的地址,函数按照顺序将test中的元素复制到test1中。
注意:不能写成的形式,因为sizeof(test)得到的是vector的大小,这是一个固定值,和其中存储的值的数量、类型(除了bool)无关。和我们实际想要复制的长度不同,会引发各种各样的错误。
考虑vector存放类类型的情况。定义一个MyClass类,执行下列语句,确实先析构了test1中的元素,等到test调用其析构函数时,访问冲突异常。按照第一章中的方式进行修改,变量正常析构,程序正常退出。
之前提到,STL容器占的字节数和储存的数据的数目和类型(除了bool)无关,是一个固定值。用vector举例,网上很多回答都是说vector中有三个迭代器变量:start, finish, end_of_storage。这三个迭代器的类型是type*类型指针。因此vector的大小是固定的,在64位机上是24(debug版本32),在32位机上是12(debug版本16)。
并且有说vector::begin()函数返回start;vector::end()函数返回finish...不知道是不是版本或者编译器的问题,至少在我在vs2017中,sizeof(vector)的结果有多种,尝试了一些常见的类型:
观察上述运行结果,除了bool很特殊之外,其他的似乎和上述说法吻合。然而,当我执行下列语句时:
只有当type不为bool且在release版本下才满足上述说法。
bool类型的变量占1个字节,但是bool类型只有两个取值false和true.因此,为了提高效率,在vector中,bool变量是按位储存的。为了实现这一优化,vector除了有一般vector的成员变量外,还有两个额外的成员变量:
在64位机上,这两个变量都占8个字节,因此在debug版本下,vector比vector多占16个字节。release版本下可能只有其中一个变量,具体是哪个我也还不清楚。
一般的vector对象在使用[]运算符时,返回一个类型为OtherType&的左值;而vector对象访问一个代理引用(proxy reference)而非真正的引用(true reference),并返回一个类型为_Vb_reference<_Wrap_alloc>>的右值,因此不能用该返回值初始化一个bool&:
虽然vb[0]不是一个左值,但是仍然可以通过修改它的值来修改vb:
一种说法是:一般的vector只有一个迭代器:_Compressed_pair<_Alty, _Scary_val> _Mypair,它在64位机器上占24个字节,begin(),end()函数都返回它。因此debuge版本下sizeof(vector::begin())的值为24.这种说法仅供参考。
谈vector的特殊性——为什么它不是STL容器
c语言中memcpy是字节大小,使用memcpy函数时要注意拷贝数据的长度
C++之strcpy、memcpy、memmove比较