STL 相关使用

来源:互联网 发布:excel标注重复数据 编辑:程序博客网 时间:2024/06/05 15:12
(1)用vector存储指针的内容;并查询出指针内某数组的个数:
PSSN_COMMAND pCommand;
pCommand->szCaption;
怎样用vector 存储pCommand,并能查出pCommand->szCaption;的个数? 


vector<PSSN_COMMAND> v      把PSSN_COMMAND类型的指针存储到v中;
v.push_back(pCommand);      把pCommand存储到v中;     前两句为定义用vector储存指针的基本形式;
v.back()->szCaption;
v.size(); 
(2)用vector存储在v里的内容相当与v是个数组;
在调用v每次存储的数据时,可以用v[i]->szCaption;

(3)释放vector存储的内存;

vector<int> ivec;
ivec.push_back(123);
ivec.clear();//这个是删除ivec里的全部元素

释放vector里存储的指针元素时,要遍历vector里面存储的所有元素,编译后删除,最后调用clear函数;

for(int k=0;k<m_vSave.size();k++)
{  
delete m_vSave[k];
}
m_vSave.clear();

vector的使用

  

引自:

http://hi.baidu.com/taney/item/89dd52fb40d75ec00cd1c8d3

第一部分 使用入门
vector可用于代替C中的数组,或者MFC中的CArray,从许多说明文档或者网上评论,一般一致认为应该多用vector,因为它的效率更高,而且具备很好的异常安全性。而且vector是STL推荐使用的默认容器,除非你知道你有特殊需要,使用vector不能满足你的需求,例如需要容器在head和tail高效的插入和删除,或者在任何位置高效的删除和插入操作,那么你可能使用deque或者list更加合适。

vector是连续内存容器,换句话说,标准要求所有标准库实现的时候,vector中的元素的内存必须是连续的。所以对于插入和删除的时间复杂度是很高的,因为删除或者插入的时候,需要元素的移动,即元素复制拷贝。

vector的内部实现一般需要用到placement new ,所以效率很高,因为很多的时候,只要我们是使用得到,就可以省去很多的内存分配开销。而且vector的使用,元素可以没有默认的构造函数,但是需要拷贝构造函数的存在,这是使用CArray所无法实现的。

使用原则:

1,尽量使用vector代替C风格的数组或者CArray;

2,尽量使用算法代替手工写的循环;

3,尽量使用vector本身的函数代替其他泛型算法;

vector的接口很容易看懂和使用,这里以一些例子来说明vector的用法。

1,填充vector
如果我们想用原始数组的内容填充vector,那么于有很多种方式。我们来一次学习vector的几个方法。

例如我们有数组int v1[10] = {0,1,0,0,3,0,0,4,4,4};

初始化方式1:

vector<int> v2(10); //初始化size为10可以避免数组动态增长的时候不断的分配内存

//v2.reserve(10);//同上,只要使用其中一个就可以了

for( int i=0; i<10; i++ )

{

       v2.push_back(v1[i]);//增加一个元素

}

初始化方式2:

vector<int> v3(&v1[0],&v1[9]);//原始数组的元素指针可以作为迭代器来使用

初始化方式3:

vector<int> v4;

v4.reserve(10);

v4.insert(v4.begin(), &v1[0], &v[9]);

初始化方式4:

vector<int> v5(10);

copy(v5.begin(), &v1[0], &v1[9]);

原始数组的元素指针可以作为迭代器来使用。

原则:尽量使用reserve来减少不必要的内存分配次数。

原则:尽量使用empty而不是size()==0 来判断容器是否为空

有可能我们需要在vector中插入相应的元素

vector<int>::iterator i = find( v1.begin(), v1.end(), 3);

if( i != v1.end() )

{

       v1.insert( i, 6 );

}

2,遍历vector
例如有vector<int> v1;

void print( int i)

{

       cout << i << endl;

}

方式1:

for( int i=0; i<v1.size(); i++ )

{

       print(v1[i]);

}

这种方式是我们最熟悉的,但是不够好,写起来不够简洁。而且对于没有随机迭代器的其他容器来说,这样做是办不到的。

方式2:

typedef vector<int>:: iterator   VIntIterator;

VIntIterator end = v1.begin();

for( VIntIterator i=v1.begin(); i != end; ++i )

{

     print( *i );

}

注意:先计算end有好处,因为不必要每次去重复计算end,vector的end()不是常数时间的,所以先缓存下来能提高效率。写算法的时候尽量使用!=比较迭代器,因为<对于很多非随机迭代器没有这个操作符。

但是这种方式也写起来比较繁琐。

方式3:

for_each( v1.begin(), v1.end(), print );

使用算法写起来简单多了。

使用算法的时候,可以使用函数对象,例如

class OutPut

{

public:

       void operator ()( double i )

       {

              std::cout << i;

}

}

for_each( v1.begin(), v1.end(), OutPut );

3,vector中的删除
删除指定元素

vector<double> v1;

//….初始化代码

vector<double>:: iterator i = find( v1.begin(), v1.end(), 3.0 );

if( i != v1.end() )

{

       v1.erase(i);

}

这样就真的删除了么指定的元素了么?没有。其实只是内部的元素作了移动,vector的删除的时间复杂度是很高的。所以选择容器的时候,如果需要频繁在中间插入和删除元素,那选择vector就会影响效率了。

注意:插入或者删除操作会使得迭代器失效。

原则:使用erase-remove惯用法删除元素

v1.erase( remove(v1.begin(), v1.end(), 3.0), v1.end() );

4,vector是否为空
在判断容器是否为空的时候,使用empty()来代替size()是否为了0的检查方式,因为empty更加高效时间复杂度是常数时间的,size()时间复杂度不是常数时间的。

原则:使用empty判断标准容器是否为空

5,vector中的查找
原则:尽量使用标准容器自带的算法代替公共算法。

但是vector中并没有多少自己的算法,倒是list中有remove ,remove_if,sort,reverse等算法。

find

find_if

6,使用交换技巧来修正过剩的容量
vector<int> v1;

//…初始化v1

//…删除v1中所有的元素

但是这个时候v1的内存容量并不是0,还是很大的一块内存没有释放,如果想清空

用v1.reserve(0);到不到目标,因为reserve只能扩大内存容量,并不能减小。

vector<int> v2;

v2.swap(v1);

这个时候就是真的将之内存容量降到最低了。

6,题外话:使用算法及其他技巧
原则:不要使用auto_ptr作为模板参数来建立容器,这会产生意想不到的陷阱,因为auto_ptr的拷贝有特殊的语义

原则:避免使用vector<bool>, 因为它不能当作标准容器来用

copy

find_if

for_each

原则:尽量使用区间成员函数代替它们的单元素参数兄弟成员函数

区间构造

区间删除

区间赋值

区间插入

原则:使得容器中元素的拷贝操作轻量而正确,拷贝是STL中容器的运行的基本方式,将元素加入到容器,必然保持一个拷贝,容器中的元素不再是原来的那个元素,所以如果将指针加入容器,必须在容器离开作用域以前删除指针所指的对象。

void fun()

{

       vector<object*> v1;

       ……//插入操作

       for( vector<object*> :: iterator i = v1.begin(); i != v1.end(); ++i )

       {

              delete *i;

}

}

原则:注意对于 vector,任何插入删除操作都会引起迭代器失效。所以要小心。

第二部分 使用错误讨论
vector的语义常被误解,所以经常会出现使用错误

1,案例一
你能发现下面的问题所在么?

void func( vector<.int>& v1 )

{

       v1[0] = 1;    //#1

       v1.at(0) = 1; //#2

}

#1和#2有何区别?

void func1( vector<int>& v1 )

{

       v1.reserve(2);

       assert( v1.capacity() == 2 );

       v1[0] = 1;

    v1[1] = 2;

       for( vector<int>::iterator i = v1.begin(); i<v1.end(); i++ )

       {

              cout << *i << andl;

       }

      

       cout << v1[0];

       v1.reserve(100);

       assert(v1.capicity() == 100 );

       cout << v1[0];

       v1[2] = 3;

    v1[3] = 4;

       //……

       v[99] = 100;

       for( vector<int>::iterator i = v1.begin(); i<v1.end(); i++ )

       {

              cout << *i << andl;

       }

}

函数func1存在什么问题(丛风格和代码正确性做出评价)?会打印出什么?

2,案例二
使用vector的时候最容易发生的运行时错误,莫过于迭代器错误

注意:对于 vector,任何插入删除操作都会引起迭代器失效。所以要小心


Map的使用详解(c++)
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。 


下面举例说明什么是一对一的数据映射。比如一个班级中,每个学生的学号跟他的姓名就存在着一一映射的关系,这个模型用map可能轻易描述,很明显学号用int描述,姓名用字符串描述(本篇文章中不用char *来描述字符串,而是采用STL中string来描述),下面给出map描述代码: 


Map<int, string> mapStudent; 


1.       map的构造函数 


map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map: 


Map<int, string> mapStudent; 


2.       数据的插入 


在构造map容器后,我们就可以往里面插入数据了。这里讲三种插入数据的方法: 


第一种:用insert函数插入pair数据,下面举例说明(以下代码虽然是随手写的,应该可以在VC和GCC下编译通过,大家可以运行下看什么效果,在VC下请加入这条语句,屏蔽4786警告  #pragma warning (disable:4786) ) 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(pair<int, string>(1, “student_one”)); 


       mapStudent.insert(pair<int, string>(2, “student_two”)); 


       mapStudent.insert(pair<int, string>(3, “student_three”)); 


       map<int, string>::iterator  iter; 


       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








第二种:用insert函数插入value_type数据,下面举例说明 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(map<int, string>::value_type (1, “student_one”)); 


       mapStudent.insert(map<int, string>::value_type (2, “student_two”)); 


       mapStudent.insert(map<int, string>::value_type (3, “student_three”)); 


       map<int, string>::iterator  iter; 


       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








第三种:用数组方式插入数据,下面举例说明 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent[1] =  “student_one”; 


       mapStudent[2] =  “student_two”; 


       mapStudent[3] =  “student_three”; 


       map<int, string>::iterator  iter; 


       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的,用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值,用程序说明 


mapStudent.insert(map<int, string>::value_type (1, “student_one”)); 


mapStudent.insert(map<int, string>::value_type (1, “student_two”)); 


上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,程序如下 


Pair<map<int, string>::iterator, bool> Insert_Pair; 


Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, “student_one”)); 


我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。 


下面给出完成代码,演示插入成功与否问题 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


Pair<map<int, string>::iterator, bool> Insert_Pair; 


       Insert_Pair = mapStudent.insert(pair<int, string>(1, “student_one”)); 


       If(Insert_Pair.second == true) 


       { 


              Cout<<”Insert Successfully”<<endl; 


       } 


       Else 


       { 


              Cout<<”Insert Failure”<<endl; 


       } 


       Insert_Pair = mapStudent.insert(pair<int, string>(1, “student_two”)); 


       If(Insert_Pair.second == true) 


       { 


              Cout<<”Insert Successfully”<<endl; 


       } 


       Else 


       { 


              Cout<<”Insert Failure”<<endl; 


       } 


       map<int, string>::iterator  iter; 


       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








大家可以用如下程序,看下用数组插入在数据覆盖上的效果 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent[1] =  “student_one”; 


       mapStudent[1] =  “student_two”; 


       mapStudent[2] =  “student_three”; 


       map<int, string>::iterator  iter; 


       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








3.       map的大小 


在往map里面插入了数据,我们怎么知道当前已经插入了多少数据呢,可以用size函数,用法如下: 


Int nSize = mapStudent.size(); 


4.       数据的遍历 


这里也提供三种方法,对map进行遍历 


第一种:应用前向迭代器,上面举例程序中到处都是了,略过不表 


第二种:应用反相迭代器,下面举例说明,要体会效果,请自个动手运行程序 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(pair<int, string>(1, “student_one”)); 


       mapStudent.insert(pair<int, string>(2, “student_two”)); 


       mapStudent.insert(pair<int, string>(3, “student_three”)); 


       map<int, string>::reverse_iterator  iter; 


       for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++) 





       Cout<<iter->first<<”   ”<<iter->second<<end; 








第三种:用数组方式,程序说明如下 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(pair<int, string>(1, “student_one”)); 


       mapStudent.insert(pair<int, string>(2, “student_two”)); 


       mapStudent.insert(pair<int, string>(3, “student_three”)); 


       int nSize = mapStudent.size() 


//此处有误,应该是 for(int nIndex = 1; nIndex <= nSize; nIndex++) 




//by rainfish 


       for(int nIndex = 0; nIndex < nSize; nIndex++) 





       Cout<<mapStudent[nIndex]<<end; 








5.       数据的查找(包括判定这个关键字是否在map中出现) 


在这里我们将体会,map在数据插入时保证有序的好处。 


要判定一个数据(关键字)是否在map中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的map基本用法。 


这里给出三种数据查找方法 


第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了 


第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器,程序说明 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(pair<int, string>(1, “student_one”)); 


       mapStudent.insert(pair<int, string>(2, “student_two”)); 


       mapStudent.insert(pair<int, string>(3, “student_three”)); 


       map<int, string>::iterator iter; 


       iter = mapStudent.find(1); 


if(iter != mapStudent.end()) 





       Cout<<”Find, the value is ”<<iter->second<<endl; 





Else 





       Cout<<”Do not Find”<<endl; 








第三种:这个方法用来判定数据是否出现,是显得笨了点,但是,我打算在这里讲解 


Lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器) 


Upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器) 


例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3 


Equal_range函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字,程序说明 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent[1] =  “student_one”; 


       mapStudent[3] =  “student_three”; 


       mapStudent[5] =  “student_five”; 


       map<int, string>::iterator  iter; 


iter = mapStudent.lower_bound(2); 





       //返回的是下界3的迭代器 


       Cout<<iter->second<<endl; 





iter = mapStudent.lower_bound(3); 





       //返回的是下界3的迭代器 


       Cout<<iter->second<<endl; 









iter = mapStudent.upper_bound(2); 





       //返回的是上界3的迭代器 


       Cout<<iter->second<<endl; 





iter = mapStudent.upper_bound(3); 





       //返回的是上界5的迭代器 


       Cout<<iter->second<<endl; 









Pair<map<int, string>::iterator, map<int, string>::iterator> mapPair; 


mapPair = mapStudent.equal_range(2); 


if(mapPair.first == mapPair.second) 
       { 


       cout<<”Do not Find”<<endl; 





Else 





Cout<<”Find”<<endl; 



mapPair = mapStudent.equal_range(3); 


if(mapPair.first == mapPair.second) 
       { 


       cout<<”Do not Find”<<endl; 





Else 





Cout<<”Find”<<endl; 






6.       数据的清空与判空 


清空map中的数据可以用clear()函数,判定map中是否有数据可以用empty()函数,它返回true则说明是空map 


7.       数据的删除 


这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法 


#include <map> 


#include <string> 


#include <iostream> 


Using namespace std; 


Int main() 





       Map<int, string> mapStudent; 


       mapStudent.insert(pair<int, string>(1, “student_one”)); 


       mapStudent.insert(pair<int, string>(2, “student_two”)); 


       mapStudent.insert(pair<int, string>(3, “student_three”)); 






//如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好 


       //如果要删除1,用迭代器删除 


       map<int, string>::iterator iter; 


       iter = mapStudent.find(1); 


       mapStudent.erase(iter); 






       //如果要删除1,用关键字删除 


       Int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0 






       //用迭代器,成片的删除 


       //一下代码把整个map清空 


       mapStudent.earse(mapStudent.begin(), mapStudent.end()); 


       //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合 






       //自个加上遍历代码,打印输出吧 





8.       其他一些函数用法 


这里有swap,key_comp,value_comp,get_allocator等函数,感觉到这些函数在编程用的不是很多,略过不表,有兴趣的话可以自个研究 


9.       排序 


这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过不去,下面给出两个方法解决这个问题 


第一种:小于号重载,程序举例 


#include <map> 


#include <string> 


Using namespace std; 


Typedef struct tagStudentInfo 





       Int      nID; 


       String   strName; 


}StudentInfo, *PStudentInfo;  //学生信息 






Int main() 





    int nSize; 


       //用学生信息映射分数 


       map<StudentInfo, int>mapStudent; 


    map<StudentInfo, int>::iterator iter; 


       StudentInfo studentInfo; 


       studentInfo.nID = 1; 


       studentInfo.strName = “student_one”; 


       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90)); 


       studentInfo.nID = 2; 


       studentInfo.strName = “student_two”; 


mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80)); 






for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) 


    cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl; 









以上程序是无法编译通过的,只要重载小于号,就OK了,如下: 


Typedef struct tagStudentInfo 





       Int      nID; 


       String   strName; 


       Bool operator < (tagStudentInfo const& _A) const 


       { 


              //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序 


              If(nID < _A.nID)  return true; 


              If(nID == _A.nID) return strName.compare(_A.strName) < 0; 


              Return false; 


       } 


}StudentInfo, *PStudentInfo;  //学生信息 


第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明 


#include <map> 


#include <string> 


Using namespace std; 


Typedef struct tagStudentInfo 





       Int      nID; 


       String   strName; 


}StudentInfo, *PStudentInfo;  //学生信息 






Classs sort 





       Public: 


       Bool operator() (StudentInfo const &_A, StudentInfo const &_B) const 


       { 


              If(_A.nID < _B.nID) return true; 


              If(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0; 


              Return false; 


       } 


}; 






Int main() 





       //用学生信息映射分数 


       Map<StudentInfo, int, sort>mapStudent; 


       StudentInfo studentInfo; 


       studentInfo.nID = 1; 


       studentInfo.strName = “student_one”; 


       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90)); 


       studentInfo.nID = 2; 


       studentInfo.strName = “student_two”; 


mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80)); 





10.   另外 


由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,这里默认用的是小于号,即less<>,如果要从大到小排序呢,这里涉及到的东西很多,在此无法一一加以说明。 


还要说明的是,map中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的,如果用map函数可以实现的功能,而STL  Algorithm也可以完成该功能,建议用map自带函数,效率高一些。 


下面说下,map在空间上的特性,否则,估计你用起来会有时候表现的比较郁闷,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),我想大家应该知道,这些地方很费内存了吧,不说了……