zoukankan      html  css  js  c++  java
  • 【C++】朝花夕拾——STL vector

    STL之vector篇

      N久之前是拿C的数组实现过vector中的一些简单功能,什么深拷贝、增删查找之类的,以为vector的实现也就是这样了,现在想想真是...too young too naive...ORZ

    ====================我是分割线=============================

    vector属于顺序容器,它的底层实现就是基于array,所以它可以支持随机访问,但是它比array更有效率,因为它动态分配的内存空间

    动态分配的内存空间:

       每当vector的capacity==size,并且有新element需要加入时,vector会申请一段大小等于2*capacity的连续内存空间,将原来的数据深拷贝到新的内存地址,然后释放掉原来的内存,并添加新的element到内存中。(这一系列内存操作大大影响了vector的存储效率)

          因为,不同的环境下,vector的实现方式有所出入,所以初始化时的capacity大小不一样,但是capacity一定会大于所需的内存大小,预留一定的空间,已减少内存操作。

    以下是简单的测试:

    环境如下:

    g++ 4.8.4

    ubuntu 14.04

    test1:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下: 

    v1 size = 0
    v1 capacity = 0
    v1 max_size = 4611686018427387903
    v2 size = 0
    v2 capacity = 0
    v2 max_size = 18446744073709551615

    max_size返回vector可以保持的element个数的上限,这个上限与系统的底层实现有关,不一定可以达到(当内存不够时,不能继续分配)

    这里可以看出,此时初始化的capacity==0

    test2:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 1; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 1; ++i)v2.push_back(char(i));
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下:

    v1 size = 1
    v1 capacity = 1
    v1 max_size = 4611686018427387903
    v2 size = 1
    v2 capacity = 1
    v2 max_size = 18446744073709551615

    此时,插入一个元素,capacity==1,size也为1

    test3:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 2; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 2; ++i)v2.push_back(char(i));
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下:

    v1 size = 2
    v1 capacity = 2
    v1 max_size = 4611686018427387903
    v2 size = 2
    v2 capacity = 2
    v2 max_size = 18446744073709551615

    当插入两个元素时,capacity = 2 * capacity(即为2),然后拷贝之前的数据到新的内存空间,添加新元素,则size==2

    test4:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 3; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 3; ++i)v2.push_back(char(i));
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下:

    v1 size = 3
    v1 capacity = 4
    v1 max_size = 4611686018427387903
    v2 size = 3
    v2 capacity = 4
    v2 max_size = 18446744073709551615

    又接着添加一个新元素,capacity翻倍(变成4),size增加1,即3

    添加新元素的原理get  >   <

    ===========================第二分割线===================================

    以下为删除原理

    test5:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 129; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 129; ++i)v2.push_back(char(i));
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下:

    v1 size = 129
    v1 capacity = 256
    v1 max_size = 4611686018427387903
    v2 size = 129
    v2 capacity = 256
    v2 max_size = 18446744073709551615

    test6:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 129; ++i)v1.push_back(i);
        v1.pop_back();
        v1.pop_back();
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 129; ++i)v2.push_back(char(i));
        v2.pop_back();
        v2.pop_back();
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果如下:

    v1 size = 127
    v1 capacity = 256
    v1 max_size = 4611686018427387903
    v2 size = 127
    v2 capacity = 256
    v2 max_size = 18446744073709551615

    (⊙v⊙)嗯,看来内存的动态改变并不会随着size的减小的缩减capacity,避免了不必要的内存操作。

    C++手册中也有说明,pop_back的操作只是size减1

    =============================第三分割线==================================

    接着,是各个函数的底层理解

    assign() 

      有三种形参形式(iterator begin,iterator end)初始化为迭代器指向的容器之间的内容;(n, value)初始化N个值为value的元素;(initializer_list<value_type> il)深拷贝

    back()

      返回vector中最后一个值,当vector为空时,调用该函数会报错

    front()

      返回vector中第一个值,当vector为空时,调用该函数会报错

    begin()

      返回指向第一个element的迭代器

    end()

      返回指向past-the-end element的迭代器,past-the-end element是理论上应该跟在最后一个element后面的位置,但是那个位置上并没有存放element

    cbegin()

      返回一个首个元素的const_iterator迭代器,用该迭代器访问元素是是不可以用来改变元素本身值的(即便元素本身并非const类型)

    cend()

      返回一个past-the-last element的const_iterator迭代器

    crbegin()

      r表示逆序,c表示const,此处返回vector中逆序第一个element的const_iterator

    crend()

      此处返回vector中第一个element的const_iterator,功能与cbegin()相同

    rbegin()

      返回逆序第一个element的迭代器,即为指向end()的前一个位置的迭代器

    rend()

      返回逆序的最后一个element的迭代器,即指向第一个element

    capacity()

      返回vector容器已开辟的内存可存放element的个数

    size()

      返回当前vector中已有的element个数,size <= capacity

    max_size()

      返回一个理论上允许的capacity上限值,但不一定能达到

    data()

      返回一个指向当前容器内存起始地址的指针,由于vector的内存分配是连续的,所以可以直接用指针offset来为容器中的element赋值

      std::vector<int> myvector (5);

      int* p = myvector.data();

      *p = 10;

      ++p;

      *p = 20;

        p[2] = 100;

    emplace()

     关于emplace和insert的区别,如下:

    (emplace会调用类的构造函数,代码搬运自:http://stackoverflow.com/questions/14788261/c-stdvector-emplace-vs-insert)

    struct Foo
    {
      Foo(int n, double x);
    };
    
    std::vector<Foo> v;
    v.emplace(someIterator, 42, 3.1416);
    v.insert(someIterator, Foo(42, 3.1416));

    emplace_back()

      调用构造函数,然后把element加到末尾

    insert()

      可以把element插入到迭代器指定的element之前的位置。

      由于底层实现时array,除了在末尾插入element,在其他的位置插入,都需要将指定插入位置后面的element移位,这将导致效率低下

      如果插入之后size>capacity,则参考push_back的重新申请内存空间

    erase()

      删除指定区间或者位置的element,同样,除了删除末尾的element,删除其他位置的element都需要把后面的element往前移动,也是导致效率低的原因

    clear()

      把所有element都移除,size=0,此时capacity不一定为0(不一定会有释放内存的操作)//正确的释放内存姿势——swap()

    empty()

      size >= 0, 返回false;否则返回true

    get_allocator()

      返回该容器的allocator

    operator=

      拷贝,STL中的拷贝都是深拷贝

    operator[]

      访问节点

    push_back()

      在末尾增加一个element,当size超出时,capacity翻倍(具体看此博文开头),size++

    pop_back()

      移除末尾一个element, size--

    reserve()

      v.reserve(n);表示此时V的capacity至少要大于等于300,若capacity<300,则重新申请内存;反之则不用做其他操作;

      

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 129; ++i)v1.push_back(i);
        v1.pop_back();
        v1.pop_back();
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        vector<char> v2;
        for (int i = 0; i < 129; ++i)v2.push_back(char(i));
        v2.reserve(300);
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
        return 0;
    }

    结果为:

    v1 size = 127
    v1 capacity = 256
    v1 max_size = 4611686018427387903
    v2 size = 129
    v2 capacity = 300
    v2 max_size = 18446744073709551615

    resize()

      v.resize(n)将会把v中的size调整为n大小,若n > size,则用0补足element;若n < size,则取容器中的前n个element,其与元素移除;

      v.resize(n,m)将会把v中的size调整为n大小,若n > size,则用m补足element

      

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 10; ++i)v1.push_back(i);
        for (int i = 0; i < v1.size(); i ++)cout << v1[i] << " ";
        cout << endl;
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        v1.resize(7);
        for (int i = 0; i < v1.size(); i ++)cout << v1[i] << " ";
        cout << endl;
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        v1.resize(20, 200);
        for (int i = 0; i < v1.size(); i ++)cout << v1[i] << " ";
        cout << endl;
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        v1.resize(50);
        for (int i = 0; i < v1.size(); i ++)cout << v1[i] << " ";
        cout << endl;
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        return 0;
    }

    结果如下:

    0 1 2 3 4 5 6 7 8 9
    v1 size = 10
    v1 capacity = 16
    v1 max_size = 4611686018427387903
    0 1 2 3 4 5 6
    v1 size = 7
    v1 capacity = 16
    v1 max_size = 4611686018427387903
    0 1 2 3 4 5 6 200 200 200 200 200 200 200 200 200 200 200 200 200
    v1 size = 20
    v1 capacity = 20
    v1 max_size = 4611686018427387903
    0 1 2 3 4 5 6 200 200 200 200 200 200 200 200 200 200 200 200 200 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    v1 size = 50
    v1 capacity = 50
    v1 max_size = 4611686018427387903

    shrink_to_fit()

      一般来说capacity满足,capacity>=size;v.shrink_to_fit()操作可以使得,capacity==size,这也是释放内存的操作之一!!!

      

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 10; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        v1.resize(7);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        v1.shrink_to_fit();
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        return 0;
    }

    结果如下:

    v1 size = 10
    v1 capacity = 16
    v1 max_size = 4611686018427387903
    v1 size = 7
    v1 capacity = 16
    v1 max_size = 4611686018427387903
    v1 size = 7
    v1 capacity = 7
    v1 max_size = 4611686018427387903

    swap()

      v.swap(v2)的操作可以将v,和v2中的element交换

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 10; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
    
        vector<int> v2;
        for (int i = 0; i < 20; ++i)v2.push_back(i);
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
    
        v1.swap(v2);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        cout << "v1 max_size = " << v1.max_size() << endl;
        cout << "v2 size = " << v2.size() << endl;
        cout << "v2 capacity = " << v2.capacity() << endl;
        cout << "v2 max_size = " << v2.max_size() << endl;
    
        return 0;
    }

    结果如下:

    v1 size = 10
    v1 capacity = 16
    v1 max_size = 4611686018427387903
    v2 size = 20
    v2 capacity = 32
    v2 max_size = 4611686018427387903
    v1 size = 20
    v1 capacity = 32
    v1 max_size = 4611686018427387903
    v2 size = 10
    v2 capacity = 16
    v2 max_size = 4611686018427387903

    小结:

    vector是不会自动释放内存的,但是如果size的极大值很大的话会造成capacity很大,浪费内存。

    所以以下有几种方法可以释放vector的内存:

      ①shrink_to_fit

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 20; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
    
        v1.clear();
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
    
        v1.shrink_to_fit();
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
        
    
        return 0;
    }

    结果:

    v1 size = 20

    v1 capacity = 32

    v1 size = 0
    v1 capacity = 32
    v1 size = 0
    v1 capacity = 0

      

      ②swap

      

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v1;
        for (int i = 0; i < 20; ++i)v1.push_back(i);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
    
        vector<int>().swap(v1);
        cout << "v1 size = " << v1.size() << endl;
        cout << "v1 capacity = " << v1.capacity() << endl;
    
        return 0;
    }

    结果如下:

    v1 size = 20
    v1 capacity = 32
    v1 size = 0
    v1 capacity = 0

     

        ③指针释放

        

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<int> v;
        vector<int>* v1 = &v;
        for (int i = 0; i < 20; ++i)v1->push_back(i);
        cout << "v size = " << v.size() << endl;
        cout << "v capacity = " << v.capacity() << endl;
    
        delete v1;
        return 0;
    }

    ========================我是第四分割线============================

    C++11的标准中,vector新增加的成员函数:

    crbegin

    crend

    cbegin

    cend

    emplace

    emplace_back

    data

    shrink_to_fit

    ========================我是第五分割线============================

    最后简单说说,高效索引之bitset和vector<bool>

    bitset  可以进行位操作,但是大小固定

    vector<bool>   属于vector的一种特殊形式,用bool类型的allocator构造,但是在实现的时候优化了,所以它的element类型并不是bool,而是bit

  • 相关阅读:
    Linux 高性能server编程——高级I/O函数
    中国儿童移动游戏市场解读 潜力巨大有待开发
    HDU 2152 Fruit (母函数)
    Currying vs Partial Application
    我的最爱Lambda演算——开篇
    函数式编程-数据结构+算法
    高阶函数 、高阶类型
    高阶函数-哈哈
    备份-泛函编程(23)-泛函数据类型-Monad
    高阶函数
  • 原文地址:https://www.cnblogs.com/cheermyang/p/5341328.html
Copyright © 2011-2022 走看看