STL学习笔记vector

STL学习笔记--vector

vector 向量容器

    作为数组的一个泛化推广的 vector 容器,不仅可以进行数组一样的元素随机访问,还可以在容器的尾端插入新元素,是一个实现了 Random Access Container 和 Back Insertion Sequence 概念的模型
    vector 是一种简单、高效的容器。在尾端插入和删除元素,算法时间复杂度为 O(1) 常数阶,其他元素的插入删除为 O(n) 线型阶,其中 n 为 vector 容器的元素个数。 vector 具有自动的内存管理功能,对于元素的插入和删除,可动态调整所占用的内存空间。


创建 vector 对象
如下4个vector的构造函数,均可创建一个vector对象
1.    vector(const A& a = A())
    创建一个空的 vector 对象,A 是内存分配器,此参数可以省略,相当于一个 vector() 的调用。
例如,下面一行代码创建了一个 vector 对象 vInt
    vector<int> vInt;  

2.    vector(size_type n)
     创建一个具有 n 个元素的 vector 对象,每个 vector 元素具有它的类型下的默认值。此时, n 个 vector 元素的内存空间已被分配。
例如,下面一行代码创建了具有 10 个元素的 vector 对象 vDou,每个元素的默认值为 0.0
    vector<double> vDou(10);
    
3.    vector(size_type n, const T& value)
    创建一个具有 n 个元素的 vector 对象,每个元素具有初始值 value 。
例如,下面一行代码创建了一个具有 10 个元素的 vector 对象 vDou, 每个元素的初始值为 9.3
    vector<double> vDou(10, 9.3);
    
4.    vector(const vector&)
    通过拷贝一个 vector 对象的各个元素值,创建一个新的 vector 对象。
例如,下面使用 v1 对象 创建 v2 对象,此时,v2 对象的 5 个元素也具有字符值 "K"
    vector<char> v1(5,'K');
    vector<char> v2(v1);

这是第五种创建方法    
5.    vector(const InputInerator first, const InputIterator last, const A& a = A())
    InputIterator 为输入迭代器,通过拷贝迭代器区间 [first, last ) 的元素值,创建一个新的vector对象,内存分配器可省略。
例如,利用 int 数组 iArray 各元素值,创建了 vector 对象 v
    int iArray[] = {11, 13, 19, 23, 28};
    vector<int> v(iArray, iArray + 5);



初始化赋值
vector 提供的 push_back() 函数,常用来进行 vector 容器的初始化。 push_back() 函数在容器的尾端插入新元素。


元素的遍历访问
 vector 的元素可以采用数组,.at() 函数或者迭代器的方式进行遍历访问。

用数组方式访问 vector 元素
1 #include <vector>
 2 #include <iostream>
 3 using namespace std;
 4 int main()
 5 {
 6     vector<int> vInt;
 7     vInt.push_back(20);
 8     vInt.push_back(26);
 9     vInt.push_back(39);
10     
11 //    cout << "VInt【2】 = " << vInt[2] <<endl;
12 //    cout << "VInt【3】 = " << vInt[3] <<endl;
13 //    cout << "VInt【4】 = " << vInt[4] <<endl << endl << endl;
14 //    
15 //    cout << "VInt【2】 = " << vInt.at(2) <<endl;    
16 //    cout << "VInt【3】 = " << vInt.at(3) <<endl;
17 //    cout << "VInt【4】 = " << vInt.at(4) <<endl;
18     
19 
20     for (int i=0; i<vInt.size(); i++)
21     {
22         // vInt[i] 为数组方式的访问。访问越界时,不产生异常
23         //cout << "VInt【" << i << "】 = " << vInt[i] <<endl;
24         
25         // vInt.at(i) 为函数访问方式。当访问越界时,会抛出异常
26         cout << "VInt【" << i << "】 = " << vInt.at(i) <<endl;
27     }
28 
29 
30     return 0;
31 }
用迭代器访问 vector
1 /*        解释:
 2     迭代器方式的访问就是使用 vector 容器提供的 iterator 类型,定义一个迭代器变量,例如:vector<int>::iterator i;
 3 然后,对迭代器进行 "++" 操作,将迭代器从一个元素位置移动到下一个元素位置,从而通过迭代器的 "*" 操作,将所有元素读出来。
 4     vector 提供了 begin(), end() 函数,用于获取首元素的迭代器和最后一个元素的下一个位置的迭代器
 5 */
 6 
 7 ------------------------------------------------------- 用迭代器访问 vector 元素
 8 #include <vector>
 9 #include <iostream>
10 using namespace std;
11 int main()
12 {
13     vector<int> vInt;
14     vInt.push_back(20);
15     vInt.push_back(26);
16     vInt.push_back(39);
17 
18     // 起始迭代器值 
19     vector<int>::iterator i;
20     int j;
21     // vInt不改变,可以在for 循环中,将i!=vInt.end()作为判断条件
22     for (i=vInt.begin(), j=0; i!=vInt.end(); i++, j++)
23     {
24         cout << "v[" << j << "] = " << *i << endl;
25     }
26 
27     return 0;
28 }
在任意位置插入 vector 元素
1 -------------------------------- 在任意位置插入 vector 元素
 2 #include <vector>
 3 #include <iostream>
 4 using namespace std;
 5 int main()
 6 {
 7     vector<int> vInt;
 8     vInt.push_back(6);
 9     vInt.push_back(7);
10     vInt.push_back(8);
11     vInt.push_back(10);
12     // 在元素 10 的前面插入 9
13     vInt.insert(vInt.begin() + 3, 9);
14     // 插入 5 为首元素
15     vInt.insert(vInt.begin(), 5);
16     // 插入 11 为末元素
17     vInt.insert(vInt.end(), 11);
18 
19     for (int i=0; i<vInt.size(); i++)
20     {
21         cout << "V[" << i << "]=" << vInt[i] << endl;
22     }
23 
24     return 0;
25 }
利用 erase 函数删除 vector 元素
1 ---------------------------------------- 利用 erase 函数删除 vector 元素
 2 #include <iostream>
 3 #include <vector>
 4 using namespace std;
 5 class MyAnimal
 6 {
 7 public:
 8     char* m_name;
 9     int m_age;
10 public:
11     MyAnimal(char* name, int age)
12     {
13         this->m_name = name;
14         this->m_age = age;
15     }
16     ~MyAnimal(){}
17 
18 };
19 
20 int main()
21 {
22     MyAnimal* pDog = new MyAnimal("dog", 1);
23     MyAnimal* pMonkey = new MyAnimal("monkey", 2);
24     MyAnimal* pChicken = new MyAnimal("chicken", 3);
25     MyAnimal* pSnake = new MyAnimal("snake", 4);
26 
27     // v 将存放各对象的地址
28     vector<MyAnimal*> v;
29     v.push_back(pDog);
30     v.push_back(pMonkey);
31     v.push_back(pChicken);
32     v.push_back(pSnake);
33 
34     // 物理删除 pMonkey 所指的对象
35     delete pMonkey;
36     // 删除第 2 个元素,即删除 vector 容器的元素 pMonkey
37     v.erase(v.begin()+1);
38     vector<MyAnimal*>::iterator i, iend;
39     iend = v.end();
40     for (i=v.begin(); i!=iend; ++i)
41     {
42         cout << (*i)->m_name << ' ' << (*i)->m_age <<endl;
43     }
44     // 清除所有 vector 元素
45     v.clear();
46     cout << " 执行 clear() " << endl << "vector 元素已全部清除。" <<endl;
47 
48     return 0;
49 }
反向遍历 vector 的元素
1 -------------------------------------------------- 反向遍历 vector 的元素
 2 #include <vector>
 3 #include <iostream>
 4 using namespace std;
 5 int main()
 6 {
 7     vector<int> vInt;
 8     vInt.push_back(1);
 9     vInt.push_back(3);
10     vInt.push_back(5);
11     vInt.push_back(7);
12     vInt.push_back(9);
13     // 元素的反向遍历访问
14     vector<int>::reverse_iterator ri,riend;
15     riend = vInt.rend();
16     for (ri=vInt.rbegin(); ri!=riend; ++ri)
17     {
18         cout << *ri << endl;
19     }
20 
21     return 0;
22 }
两个 vector 容器元素的交换
1 -------------------------------------------------- 两个 vector 容器元素的交换
 2 #include <vector>
 3 #include <iostream>
 4 using namespace std;
 5 void print(vector<int>& vInt);
 6 int main()
 7 {
 8     //vIntA
 9     vector<int> vIntA;
10     vIntA.push_back(11);
11     vIntA.push_back(12);
12     vIntA.push_back(13);
13     cout << " vIntA = ";
14     print(vIntA);
15 
16     //vIntB
17     vector<int> vIntB;
18     vIntB.push_back(90);
19     vIntB.push_back(92);
20     cout<< " vIntB = ";
21     print(vIntB);
22 
23     // vIntA 与 vIntB 交换
24     swap(vIntA, vIntB);
25     cout << "vIntA 与 vIntB 交换后" << endl;
26     cout << "vIntA =";
27     print(vIntA);
28     cout << "vIntB =";
29     print(vIntB);
30 
31     return 0;
32 }
33 
34 // vector 元素打印
35 void print(vector<int>& v)
36 {
37     for (int i=0; i<v.size(); i++)
38         cout<< v[i] << "  " ;
39     cout << endl;
40 }
vector 的其他常用函数
 1 /*        解释:
 2 1. bool empty()  判断容器 vector 是否为空,若容器没有一个元素则返回 true, 否则返回 false
 3 2. size_type size()  当前 vector 容器的实际元素个数
 4 3. size_type max_size()  系统所允许的 vector 容器的最大元素个数
 5 4. size_type capacity()  当前可容纳的 vector 元素个数
 6 5. reference front()  vector容器的首元素(引用),要求 vector 不为空
 7 6. reference back()  vector容器的末元素(引用),要求 vector 不为空
 8 7. void pop_back()  与 push_back() 函数相反,pop_back() 函数用于删除末尾的一个容器元素
 9 */
10 
11 -------------------------------- vector 的其他常用函数
12 #include <vector>
13 #include <iostream>
14 using namespace std;
15 void print(vector<int>& v);
16 int main()
17 {
18     vector<int> vInt;
19     print(vInt);
20     // 添加 5 个元素
21     vInt.push_back(1);
22     vInt.push_back(2);
23     vInt.push_back(3);
24     vInt.push_back(4);
25     vInt.push_back(5);
26     print(vInt);
27 
28     // 再添加 4 个元素
29     vInt.push_back(6);
30     vInt.push_back(7);
31     vInt.push_back(8);
32     vInt.push_back(9);
33     print(vInt);
34 
35     // 调整 vector 数据空间大小
36     vInt.reserve(30);
37     print(vInt);
38 
39     return 0;
40 }
41 
42 void print(vector<int>& v)
43 {
44     cout << "----------------------" << endl;
45     cout << "empty = " << v.empty()<< endl;
46     cout << "size = " << v.size()<< endl;
47     cout << "max_size = " << v.max_size()<< endl;
48     cout << "capacity = " << v.capacity()<< endl;
49 
50 }

------------ vector 小结:
    vector 向量容器是一个实现数据线型存储的泛型类,除了可以使用数组方式进行元素访问外,还可以利用前向和反向迭代器
iterator/reverse_iterator ,以及 push_back 、 begin 、 end 、 erase 和 clear 等函数,对容器元素进行插入、遍历和删除操作。

    vector 缺点: vector 不适合那种 【容器元素 插入 删除 操作非常频繁】 的情况
    vector 优点: 看最前面的介绍

【好好学习,天天向上】 ||||||| 【 亮仔 到此一游 】
 
标签: STLC++容器vector
对于STL的掌握, 侯捷将境界分为三层: 会用,明理,能扩展。 我自己在学习STL的过程中也有类似体会,为避免初学者走弯路, 下面是个人的一些学习经验和参考书籍:
《C++标准程序库:自修教程与参考手册》这本书既是STL学习的入门书,也是日后的重要参考手册,遇到任何STL用法方面的问题,基本上都可以在这本书上找到答案。
《Effective STL》 如果说前面这本书让你使用STL入门, 那么这本书是告诉你如何高效的使用STL以及如何规避STL的缺陷和陷阱。

看完前面的2本书, 在实际工作中尽量多用STL,经过一段时间, 基本上已经到达 "会用" 的境界了。

在 “明理” 阶段,个人推荐看《泛型编程与STL》,这本书是STL的著者写的, 他把STL的设计理念和架构层次解释的非常清楚,内部详细描述了STL的各种泛型需要满足的concepts, 该书也是STL实作是否符合标准的参考手册。个人建议即使你只关注“会用”STL, 也看一下这本书, 这本书会让你认识STL的本质。

最后一个阶段是扩展, 甚至自己重写STL, 参考书是《STL源码剖析》, 这本书是个人学习STL源码的绝佳书籍, 强烈推荐。当然看STL源码需要有一定的 “模板” 功力, 如果功力不够,可以先看下《C++ Templates》, 这是一本学习模板编程的标准书。

个人尝试山寨了下STL, 对STL的6大组件(containers, algorithms, iterators, functors, adaptors, allocators)都有涉及。 当然山寨STL不是为了重复造轮子,而是为了更好的理解和扩展STL。

源码下载: SimpleSTL
 
 
分类: STL&GP
标签: STL C++

STL学习笔记

STL学习笔记--vector
摘要: vector 向量容器 作为数组的一个泛化推广的 vector 容器,不仅可以进行数组一样的元素随机访问,还可以在容器的尾端插入新元素,是一个实现了 Random Access Container 和 Back Insertion Sequence 概念的模型 vector 是一种简单、高效的容器。在尾端插入和删除元素,算法时间复杂度为 O(1) 常数阶,其他元素的插入删除为 O(n) 线型阶,其中 n 为 vector 容器的元素个数。 vector 具有自动的内存管理功能,对于元素的插入和删除,可动态调整所占用的内存空间。 vector 容器的 C++ 标准头文件为 vector, 因此.阅读全文

posted @ 2013-04-03 19:50 music__liang 阅读(52) | 评论 (0) 编辑

STL学习笔记--前篇
摘要: 终于,我又开始更新博客了。。。之所以没更新,是因为没学到什么知识,马上就毕业了,必须【闭关】学点东西啊。 以前 ,总觉得 STL 这类东西,等毕业了在去学还来得及。。。但是,笔试了几家公司之后,觉得很多公司都看重这方面的知识, STL 应该在【毕业之前】就学习。 还有,就是觉得 STL 好抽象,不知道从哪开始。 大家都一样的,面对未知的东西,在学习或者操作之前,我们总是会感到迷茫,这很正常。。只要能坚持,能不断去寻找适合的学习的资料、学习的方法,我们总会有突破的。。。哎。这个,只能靠自己了。多看书,敲代码吧。。我也正在这样进行中。。。 在学习 STL 之前,总得有点模板的概念吧。。...阅读全文

posted @ 2013-04-03 15:39 music__liang 阅读(26) | 评论 (0) 编辑

C++
 
C++中基于Crt的内存泄漏检测
摘要: 尽管这个概念已经让人说滥了 ,还是想简单记录一下, 以备以后查询。阅读全文
posted @ 2013-02-25 22:33 Richard Wei 阅读(963) | 评论 (1) 编辑
 
在C++中实现事件(委托)(续)
摘要: 在上文 在C++中实现事件(委托) 中我们实现的C#里委托方式的事件处理, 虽然使用很方便,但是感觉似乎少了一点C#的味道, 下面我们尝试把它改成真正的C#版。 其实要改成真正的C#版,我们主要要做2件事, 一是吧CEventHandler放到外面,可以让外部直接构造, 二是实现operator +=和operator -=阅读全文
posted @ 2013-01-31 17:46 Richard Wei 阅读(1077) | 评论 (5) 编辑
 
在C++中实现事件(委托)
摘要: 在C++中实现回调机制的几种方式一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟, 实现起来很复杂。但是现在借助C++11的function和bind, 我们可以很方便的去实现。阅读全文
posted @ 2013-01-31 14:23 Richard Wei 阅读(661) | 评论 (2) 编辑
 
C++模板会使代码膨胀吗
摘要: 通过上面的分析 ,相信我们知道了为什么ATL/WTL大量使用模板,但是生成的exe还是这么小的原因 : 不是模板不会使代码膨胀,而是ATL/WTL在设计时就关注了这个问题 ,它避免了在可能生成很多模板实例的模板类中编写大量代码(有些拗口,不知道你有没有读懂^_^) 总结下 ,如果你想用模板,但是又不想 让自己最终的可执行文件变的很大, 有2种方式: (1)你的模板类不会生成很多模板实例,这样写成模板类还有意义吗? (2)你的模板类的代码量或是函数个数很少,你可以仿照ATL的方式把模板无关的东西逐层剥离。阅读全文
posted @ 2012-11-08 22:56 Richard Wei 阅读(921) | 评论 (6) 编辑
 
重构ATL中的CAutoVectorPtr, CAutoPtr和CAutoStackPtr
摘要: 看到ATL中有3个类的代码比较比较重复,在atlbase.h中,分别是CAutoVectorPtr,CAutoPtr和CAutoStackPtr,他们的功能其实很类似STL中的autoptr, 但是这里因为针对不同的分配对象而用了3个不同的类,其中CAutoVectorPtr是针对数组类型的,CAutoPtr是针对普通的非数组类型,而CAutoStackPtr针对的是_malloca分配的类型,因为最后释放方式的不同,它这里用了3份代码来实现。CAutoVectorPtr:template<typenameT>classCAutoVectorPtr{public:CAutoVect阅读全文
posted @ 2012-09-24 23:10 Richard Wei 阅读(990) | 评论 (5) 编辑
 
探索C++对象模型
摘要: 总之,拿着一把刀,庖丁解牛般的剖析语言背后的实现细节,看起来不是那么实用,但是它能让你对语言的理解更深刻。实际上ATL中大量应用上面的技术,如果没有对C++ 对象模型有比较深刻的理解,是很难深入下去的。阅读全文
posted @ 2012-09-21 23:08 Richard Wei 阅读(1538) | 评论 (3) 编辑
 
理解C++变量存储模型
摘要: 通过上面的分析,我们验证了平时C++书上关于各种类型变量存储区域的假设,简单来说就是全局变量和静态变量会被编译到可执行文件的数据节(分只读和可读写)中, 非静态的局部变量则分配在堆栈(stack)上,而new(malloc)出来的内存则分配在堆(heap)上。阅读全文
posted @ 2012-09-20 22:02 Richard Wei 阅读(1259) | 评论 (5) 编辑
 
C/C++中可变参数的原理
摘要: 从上面的例子我们可以看到,对于可变参数的函数,有2种东西需要确定,一是可变参数的数量, 二是可变参数的类型,上面的例子中,参数数量我们是在第一个参数指定的,参数类型我们是自己约定的。这种方式在实际使用中显然是不方便,于是我们就有了_vsprintf, 我们根据一个格式化字符串的来表示可变参数的类型和数量,比如C教程中入门就要学习printf, sprintf等。 总的来说可变参数给我们提供了很高的灵活性和方便性,但是也给会造成不确定性,降低我们程序的安全性,很多时候可变参数数量或类型不匹配,就会造成一些不容察觉的问题,只有更好的理解它背后的原理,我们才能更好的驾驭它。阅读全文
posted @ 2012-09-18 00:29 Richard Wei 阅读(1395) | 评论 (1) 编辑
 
C++中模块(Dll)对外暴露接口的几种方式
摘要: 当然,上面几种DLL对外暴露接口的方式本质上没有区别,都是利用PE文件的导出节来导出数据和函数,但是根据它们使用方式的不同,对外部模块来说还是有很大的区别,我们的推荐次序依次是:COM方式->导出API函数方式->导出类方式。阅读全文
posted @ 2012-08-29 19:04 Richard Wei 阅读(1301) | 评论 (0) 编辑
 
C++中实现回调机制的几种方式
摘要: 最后简单比较下上面3种实现回调的方法: 第一种Callback的方法是面向过程的,使用简单而且灵活,正如C语言本身。 第二种Sink的方法是面向对象的,在C++里使用较多, 可以在一个Sink里封装一组回调接口,适用于一系列比较固定的回调事件。 第三种Delegate的方法也是面向对象的,和Sink封装一组接口不同,Delegate的封装是以函数为单位,粒度比Sink更小更灵活。阅读全文
posted @ 2012-08-28 12:43 Richard Wei 阅读(1562) | 评论 (7) 编辑
 
C++11新特性不完全测试
摘要: 摘要: Lambda, auto, 统一初始化,智能指针,Regex, Random, function and bind, hash_map… 右值引用和Move语义, 并发(多线程库)…发布阅读全文Richard Wei 2012-06-06 17:34 发表评论阅读全文
posted @ 2012-06-06 17:34 Richard Wei 阅读(34) | 评论 (0) 编辑
 
如何判断一个C++对象是否在堆上
摘要: 摘要: 在帖子 "如何判断一个C++对象是否在堆栈上” 中, 又有人提出如何判断一个C++对象是否在堆上。阅读全文Richard Wei 2012-05-12 14:30 发表评论阅读全文
posted @ 2012-05-12 14:30 Richard Wei 阅读(38) | 评论 (0) 编辑
 
如何判断一个C++对象是否在堆栈上
摘要: 摘要: 要解答这个问题,其实就是要知道的堆栈的起始地址, 而我们知道堆栈其实就是一段有相同属性的内存页面阅读全文Richard Wei 2012-05-12 10:57 发表评论阅读全文
posted @ 2012-05-12 10:57 Richard Wei 阅读(35) | 评论 (0) 编辑
 
一个高效的内存池实现
摘要: 摘要: 在高效C++编程中看到一个不错的内存池实现方案,这里共享下,大家看看有什么不足。阅读全文Richard Wei 2012-05-05 23:23 发表评论阅读全文
posted @ 2012-05-05 23:23 Richard Wei 阅读(60) | 评论 (0) 编辑
 
引用计数的智能指针的实现与思考
摘要: 摘要: 引用计数在软件开发中是一项非常重用的技术,它可以说是无处不,我们在不知不觉中都在和它打交道,比如 Windows上的COM和Handle, Mac上的ref句柄,脚本语言中的垃圾回收技术。阅读全文Richard Wei 2012-05-05 17:04 发表评论阅读全文
posted @ 2012-05-05 17:04 Richard Wei 阅读(38) | 评论 (0) 编辑
原文地址:https://www.cnblogs.com/Leo_wl/p/2998777.html