• 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) 编辑
  • 相关阅读:
    Python入门篇-面向对象概述
    Python的序列化与反序列化
    Python的csv文件(csv模块)和ini文件(configparser模块)处理
    使用Cloudera Manager添加Sentry服务
    Python的高级文件操作(shutil模块)
    Python的正则表达式re模块
    正则表达式基础知识
    Python语言防坑小技巧
    Python标准库-数字的处理函数(math模块)
    Python数据结构汇总
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2998777.html
Copyright © 2020-2023  润新知