c++ vector用法
Posted 东方云哲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++ vector用法相关的知识,希望对你有一定的参考价值。
在c++中,vector是一个十分有用的容器,下面对这个容器做一下总结。
1 基本操作
(1)头文件#include<vector>.
(2)创建vector对象,vector<int> vec;
(3)尾部插入数字:vec.push_back(a);
(4)使用下标访问元素,cout<<vec[0]<<endl;记住下标是从0开始的。
(5)使用迭代器访问元素.
vector<int>::iterator it; for(it=vec.begin();it!=vec.end();it++) cout<<*it<<endl;
(6)插入元素: vec.insert(vec.begin()+i,a);在第i+1个元素前面插入a;
(7)删除元素: vec.erase(vec.begin()+2);删除第3个元素
vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始
(8)向量大小:vec.size();
(9)清空:vec.clear();
2
vector的元素不仅仅可以使int,double,string,还可以是结构体,但是要注意:结构体要定义为全局的,否则会出错。下面是一段简短的程序代码:
#include<stdio.h> #include<algorithm> #include<vector> #include<iostream> using namespace std; typedef struct rect { int id; int length; int width;
//对于向量元素是结构体的,可在结构体内部定义比较函数,下面按照id,length,width升序排序。
bool operator< (const rect &a) const
{
if(id!=a.id)
return id<a.id;
else
{
if(length!=a.length)
return length<a.length;
else
return width<a.width;
}
} }Rect; int main() { vector<Rect> vec; Rect rect; rect.id=1; rect.length=2; rect.width=3; vec.push_back(rect); vector<Rect>::iterator it=vec.begin(); cout<<(*it).id<<\' \'<<(*it).length<<\' \'<<(*it).width<<endl; return 0; }
3 算法
(1) 使用reverse将元素翻转:需要头文件#include<algorithm>
reverse(vec.begin(),vec.end());将元素翻转(在vector中,如果一个函数中需要两个迭代器,
一般后一个都不包含.)
(2)使用sort排序:需要头文件#include<algorithm>,
sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).
可以通过重写排序比较函数按照降序比较,如下:
定义排序比较函数:
bool Comp(const int &a,const int &b)
{
return a>b;
}
调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。
4.vector的内存增长
vector其中一个特点:内存空间只会增长,不会减小,援引C++ Primer:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。
在调用push_back时,每次执行push_back操作,相当于底层的数组实现要重新分配大小;这种实现体现到vector实现就是每当push_back一个元素,都要重新分配一个大一个元素的存储,然后将原来的元素拷贝到新的存储,之后在拷贝push_back的元素,最后要析构原有的vector并释放原有的内存。例如下面程序:
#include <iostream> #include <cstdlib> #include <vector> using namespace std; class Point { public: Point() { cout << "construction" << endl; } Point(const Point& p) { cout << "copy construction" << endl; } ~Point() { cout << "destruction" << endl; } }; int main() { vector<Point> pointVec; Point a; Point b; pointVec.push_back(a); pointVec.push_back(b); cout<<pointVec.size()<<std::endl; return 0; }
输出结果:
其中执行
pointVec.push_back(a);
此时vector会申请一个内存空间,并调用拷贝构造函数将a放到vector中
再调用
pointVec.push_back(b);
此时内存不够 需要扩大内存,重新分配内存 这时再调用拷贝构造函数将a拷贝到新的内存,再将b拷入新的内存,同时有人调用Point拷贝构造函数,最后释放原来的内存 此时调用Point的析构函数。
4 vector释放缓存
vector缓存增长的方式是:前一个容量+前一个容量*50%=下一个缓存容量
先说下vector的size和capacity函数,size函数返的是vector的实际包含元素的个数Size指目前容器中实际有多少元素,Capacity返回最少要多少元素才会使其容量重新分配。
释放缓存用vector的clear函数不行,它只是把vector里面的元素清空了,也就是size函数的返回值变为了零,但是capacity函数的返回值没有变化。用erase函数的效果同clear函数的效果同样。不能释放vector的缓存。
相信大家看到swap这个词都一定不会感到陌生,甚至会有这样想法:这不就是简单的元素交换嘛。的确,swap交换函数是仅次于Hello word这样老得不能老的词,然而,泛型算法东风,这个小小的玩意儿却在C++ STL中散发着无穷的魅力。本文不仅详细地阐述STL泛型算法swap,并借助泛型算法这股东风,展现STL容器中swap成员函数的神奇魅力。注意哦,泛型算法swap和容器中的swap成员函数,这是两个不同角度和概念哦!
(1).泛型算法swap
老规矩,我们先来看看swap的函数原型:
- template <class T> void swap ( T& a, T& b )
- {
- T c(a); a=b; b=c;
- }
函数原型超级简单吧,这里我们就不做过多的解释啦,下面我们还是通过一个简单的示例来熟悉熟悉它的使用吧。
但是vector的swap成员函数是怎么实现的?
- void swap(vector<_Tp, _Alloc>& __x) {
- __STD::swap(_M_start, __x._M_start);
- __STD::swap(_M_finish, __x._M_finish);
- __STD::swap(_M_end_of_storage, __x._M_end_of_storage);
- }
仅仅是交换了指向的首尾指针和容量指针
程序示例:
- /*******************************************************************
- * Copyright (C) Jerry Jiang
- *
- * File Name : swap.cpp
- * Author : Jerry Jiang
- * Create Time : 2012-3-24 4:19:31
- * Mail : jbiaojerry@gmail.com
- * Blog : http://blog.csdn.net/jerryjbiao
- *
- * Description : 简单的程序诠释C++ STL算法系列之十五
- * 变易算法 : 元素交换swap
- *
- ******************************************************************/
- #include <iostream>
- #include <algorithm>
- #include <vector>
- #include <iterator>
- using namespace std;
- int main ()
- {
- int x = 10, y = 20; // x:10 y:20
- swap(x, y); // x:20 y:10
- vector<int> first (4, x), second (6, y); // first:4x20 second:6x10
- swap(first, second); // first:6x10 second:4x20
- cout << "first contains:";
- //使用一般的iterator方式输出first
- for (vector<int>::iterator it=first.begin(); it != first.end(); ++it)
- {
- cout << " " << *it;
- }
- cout << endl;
- cout << "second contains: ";
- //使用copy()来实现second的输出
- copy(second.begin(), second.end(), ostream_iterator<int>(cout, " "));
- cout << endl;
- return 0;
- }
上面示例程序十分简单,只是为了巩固前文中copy算法的使用,我在程序中采用了两种方式进行输出,好了,泛型算法swap我们就不再废话了,现在我们来看看本文中的重头戏吧。
(2). 容器中的成员函数swap
在容器vector中,其内存占用的空间是只增不减的,比如说首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个。所有内存空间在vector析构时回收。
一般,我们都会通过vector中成员函数clear进行一些清除操作,但它清除的是所有的元素,使vector的大小减少至0,却不能减小vector占用的内存。要避免vector持有它不再需要的内存,这就需要一种方法来使得它从曾经的容量减少至它现在需要的容量,这样减少容量的方法被称为“收缩到合适(shrink to fit)”。(节选自《Effective STL》)如果做到“收缩到合适”呢,嘿嘿,这就要全仰仗“Swap大侠”啦,即通过如下代码进行释放过剩的容量:
- vector< T >().swap(X)
下面我们通过一个简单的示例来show一下:
- /*******************************************************************
- * Copyright (C) Jerry Jiang
- *
- * File Name : swap.cpp
- * Author : Jerry Jiang
- * Create Time : 2012-3-24 4:19:31
- * Mail : jbiaojerry@gmail.com
- * Blog : http://blog.csdn.net/jerryjbiao
- *
- * Description : 简单的程序诠释C++ STL算法系列之十五
- * 成员函数swap实现容器的内存释放
- *
- ******************************************************************/
- #include <iostream>
- #include <algorithm>
- #include <vector>
- #include <iterator>
- using namespace std;
- int main ()
- {
- int x = 10;
- vector<int> myvector(10000, x);
- //这里打印仅仅是元素的个数不是内存大小
- cout << "myvector size:"
- << myvector.size()
- << endl;
- //swap交换函数释放内存:vector<T>().swap(X);
- //T:int ; myvertor代表X
- vector<int>().swap(myvector);
- //两个输出仅用来表示swap前后的变化
- cout << "after swap :"
- << myvector.size()
- << endl;
- return 0;
- }
swap交换技巧实现内存释放思想:vector()使用vector的默认构造函数建立临时vector对象,再在该临时对象上调用swap成员,swap调用之后对象myvector占用的空间就等于一个默认构造的对象的大小,因此myvector的size就变成了0,临时对象就具有原来对象myvector的大小,而该临时对象随即就会被析构,从而其占用的空间也被释放。如果myvector是一个类的成员,不能把vector<int>().swap(myvector)写进类的析构函数中,否则会导致double free or corruption (fasttop)的错误,原因可能是重复释放内存。
std::vector<T>().swap(X)
作用相当于:
{
std::vector<T> temp(X);
temp.swap(X);
}
可以用类似的方法实现vector和string的适当收缩
- vector<int> vec(100000, 0);
- for (int i = 0; i < 100000-2; ++i) vec.pop_back();
- cout << vec.capacity() <<endl;
- vector<int>(vec).swap(vec);
- cout << vec.capacity() << endl;
注意:并不是所有的STL容器的clear成员函数的行为都和vector一样。事实上,其他容器的clear成员函数都会释放其内存。比如另一个和vector类似的顺序容器deque。
5.其他情况释放内存
如果vector中存放的是指针,那么当vector销毁时,这些指针指向的对象不会被销毁,那么内存就不会被释放。如下面这种情况,vector中的元素时由new操作动态申请出来的对象指针:
#include <vector> using namespace std; vector<void *> v;
for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++) if (NULL != *it) { delete *it; *it = NULL; } v.clear();
每次new之后调用v.push_back()该指针,在程序退出或者根据需要,用以下代码进行内存的释放:
6 vector 作为函数返回值
关于Vector作为函数的返回值,有几点需要说明:
1.首先如果Vector是一个局部的变量,那么返回该Vector的引用是十分危险的,因为在Vector超出作用域的,会自动调用相关的析构函数(~Vector()),如果Vector中存放的是类(ClassName)对象的指针,则不会调用相关的类ClassName析构函数,只会把相关的空间清空(也就是Vector.size()=0),这样会造成内存泄露。但是如果Vector中存放的是类(ClassName)的对象,则会调用相关的类ClassName析构函数。所以如果Vector是一个局部的变量,那么返回该Vector的引用是十分危险的,因为该Vector已经被析构。
例子:
|
2.返回引用的时候需要注意,不能将函数声明成 const,否则编译不通过
例如:
std::vector<className *> & GetVec() const;//声明
std::vector<className *> & className1::GetVec() const//ERROR
{ 。。。
}
//编译错误
const std::vector<className *> & GetVec() const;//声明,这样就可以了,也就是必须返回的是const的引用,才行,当然也可以两头都不用const!
3.如果不是局部变量,可以返回引用或者该Vector的迭代器(Iterator)
以上是关于c++ vector用法的主要内容,如果未能解决你的问题,请参考以下文章