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 优点: 看最前面的介绍
【好好学习,天天向上】 ||||||| 【 亮仔 到此一游 】
分类: STL学习笔记
标签: STL , C++ , 容器 , vector
山寨STL
对于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) 编辑
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息