zoukankan      html  css  js  c++  java
  • 我不熟悉的vector

    构造函数

    使用迭代器构造vector的一种方式:

    //将v[begin(), end())区间中的元素拷贝给本身
    vector(v.begin(),v.end());
    

    在这个构造函数中,传入普通数组也是可以的。如:

    int arr[] = {1,2,3,4,5};
    vector<int> v(arr,arr + sizeof(arr)/sizeof(int));
    

    assign,at函数

    这个assign函数原型有2个:

    assign(beg, end);   //将[beg, end)区间中的数据拷贝赋值给本身。
    assign(n, elem);    //将n个elem拷贝赋值给本身
    

    这个和上一篇介绍的string里面的assign效用一致,就不多说,at也一样。知道vector也有就行了。上一篇

    resize和reverse函数

    resize(int num);        //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
    resize(int num, int elem);  //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
    reserve(int len);       //容器预留len个元素长度,预留位置不初始化,元素不可访问。
    

    就如注释上说的一样。

    resize函数

    resize函数变短时,容量capacity不会缩小。

    resize函数有两个重载版本:

    • 只有一个参数int num时,表示重置容器的长度为num。若变长,则用默认值(vector为0)填充,若变短,超出末尾的元素直接被删除。
    • 参数为int num和int elem时,num还是表示重置大小,elem为新填充的元素。只有变大的时候elem才会填充。

    看以下例子以及输出结果:

    int main()
    {
    	// 构造一个vector,内含100个1,看size和capacity
    	vector<int> v(100, 1);
    	cout << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl << endl;
    	
    	// resize成5个,看size和capacity,以及打印出每个值
    	v.resize(5);
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	cout << endl << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl;
    	
    	// resize成10个,看size和capacity,以及打印出每个值
    	v.resize(10);
    	cout << endl << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl << endl;
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	
    	// resize成15个,将新位置填充为指定值,看size和capacity,以及打印出每个值
    	v.resize(13, -1);
    	cout << endl << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl << endl;
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	
    	// resize成131个
    	v.resize(131);
    	cout << endl << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl << endl;   
        return 0;
    }
    

    输出结果:

    从图中可以看出来:

    • 若resize成一个比原来小的长度时,capacity不会改变,仅仅改变size.
    • 若resize成一个比原来大的长度且capacity需要改变时,会重新分配capacity,不是与resize的长度一致,而是按照扩容的策略来。

    reverse函数

    既然有了resize,为什么还需要有reverse函数来改变大小呢?关于resize要很明确:

    • 不初始化,不初始化,不初始化!

    • 不可访问,不可访问,不可访问!

    也就是说,reserve只是开辟了这个空间,并没有去做初始化,没有初始化的位置就不能访问,否则会出现不可预料的结果。那么很明显,reserve是直接改变了capacity的值到指定大小。而resize还是采取vector的扩容策略,不断增加(以2倍或1.5倍,不同编译器不同),直到容量满足。

    reserve的例子如下:

    int main()
    {
        vector<int> v(10, 1);
    	cout << "最初:" << endl;
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	cout << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl;
    	// 第一次reserve
    	cout << endl << "reserve to 5:" << endl;
    	v.reserve(5);
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	cout << endl << "v's size = " << v.size() << endl;
    	cout << "v's capacity = " << v.capacity() << endl;
    	// 第二次reserve
    	cout << endl << "reserve to 12:" << endl;
    	v.reserve(12);
    	for (auto it = v.begin(); it != v.end(); ++it)
    		cout << *it << " ";
    	cout << endl << "v's capacity = " << v.capacity() << endl;
    	cout << "v's size = " << v.size() << endl;
        return 0;
    }
    

    结果如下:

    从这个例子可以看出:

    • reserve长度减小的时候,是不起作用的。
    • reserve长度增加的时候,会新开辟相应的空间,不做初始化,不能访问。且容量capacity就是reserve的大小,而不是采用以往的扩容策略。

    reserve的好处在哪里?

    当我们的容器容量需要比较大,且可以预估我们容器的大小的时候,最好使用reserve。

    如果我们使用普通的扩容策略,那么要扩容很多次(耗费性能,搬迁数据)才可能达到我们所需要的容量,且可能超出我们所需要容量的许多(空间冗余),等等问题。

    插入、删除函数

    erase函数

    erase(const_iterator start, const_iterator end);//删除迭代器从[start,end)之间的元素
    erase(const_iterator pos);                      //删除迭代器指向的元素
    

    还是和上一篇讲述的差不多,可以参照上一篇

    只需注意,返回的是删除之后指向的第一个元素的迭代器。

    insert函数

    几个函数原型:

    // 在迭代器position之前插入单个val
    iterator insert (const_iterator position, const value_type& val);
    // 在迭代器position之前插入n个val
    iterator insert (const_iterator position, size_type n, const value_type& val);
    // 在迭代器position之前插入迭代器first到end之间的数据,左闭右开。
    iterator insert (const_iterator position, InputIterator first, InputIterator last);
    

    注意到返回值都是迭代器类型,没错,它返回的都是指向新插入的元素的首元素的迭代器。

    以上单个插入和范围插入,均可参照上一篇。不多赘述。

    swap函数

    swap函数就是将两个容器交换。内部是直接交换指向各自vector的指针,而不是数据交换,这样节省了大量的时间。

    int main()
    {
        vector<int> v1(5, -1);
    	vector<int> v2(24, 1);
    
    	cout << "v1地址: " << &v1[0] << endl;
    	cout << "v2地址: " << &v2[0] << endl;
    
    	v1.swap(v2);
    
    	cout << "v1地址: " << &v1[0] << endl;
    	cout << "v2地址: " << &v2[0] << endl;
        return 0;
    }
    

    运行结果如下:

    由结果可见,二者地址互换。

    swap的技巧有很多,这里介绍两种:收缩空间与释放空间。

    swap收缩容器空间和释放空间

    现在考虑我们上述讲到的,我们resize时若长度变短,capacity值不会变,这样就造成了空间冗余,不需要了。我们可以用swap将它释放掉:

    int main()
    {
        vector<int> v;
    	for (int i = 0; i < 100000; i++){
    		v.push_back(i);
    	}
    	cout << "初始状态:" << endl;
    	cout << "capacity:" << v.capacity() << endl;
    	cout << "size:" << v.size() << endl;
    
    	//此时 通过resize改变容器大小
    	v.resize(10);
    
    	cout << endl << "resize to 10 之后:" << endl;
    	cout << "capacity:" << v.capacity() << endl;
    	cout << "size:" << v.size() << endl;
    
    	// swap收缩空间
    	vector<int>(v).swap(v);
    
    	cout << endl << "收缩空间:" << endl;
    	cout << "capacity:" << v.capacity() << endl;
    	cout << "size:" << v.size() << endl;
    
    	// swap释放空间
    	vector<int>().swap(v);
    
    	cout << endl << "释放空间:" << endl;
    	cout << "capacity:" << v.capacity() << endl;
    	cout << "size:" << v.size() << endl;
        return 0;
    }
    

    输出结果为:

    可以看到进行resize之后,仅仅只改变了size,如果我们接下去只需要用resize之后的空间,那么剩余的空间就成了多余的。所以我们先收缩空间,使得capacity和size一样。

    收缩空间的代码:

    vector<int>(v).swap(v);
    

    分析一下这一句做了什么操作。可看为:

    {
        vector<int> vTemp(v);
        vTemp.swap(v);
    }
    

    我们创建一个临时对象vTemp,将v中数据拷贝进去,与我们的v进行swap操作,我们的v就指向了vTemp。而vTemp出了这个作用域就被自动析构了。也就是原来的v被析构了。 所以,上面我们创建一个匿名对象,这个匿名对象的初值就是用v来拷贝的,用这个匿名对象与v进行swap操作即可。

    如果我们不再使用这个容器,可以将它释放掉。
    同理,释放空间的代码:

    vector<int>().swap(v);
    

    与上述一致,只不过这一次我们创建的匿名对象没有给初值,就会调用默认构造函数,置空了。然后我们的v再指向了这个空容器,原本的v指向了匿名对象的空间,被自动析构,达到我们想要的效果。

    补充,关于clear函数

    上面讲到可以巧用swap来进行释放空间,那clear不行吗?这也是我在上一篇没有讲到的事情。 clear函数只能做到将size清零,也没有将capacity清理掉,所以这一块内存我们还在占用。如果我们要回收,将这些都释放,使用swap就可以办法。

    这个很容易验证,创建个容器,赋值,调用clear,再查看此容器的size和capacity,会看到capacity不为0.(vector和string都是如此,但也有其它不是如此的,如deque,clear后会释放空间).

  • 相关阅读:
    关于位运算(转)
    计蒜客第三场
    数组与指针
    计蒜客第二场
    指针概念
    爬楼梯(动态规划)
    线性表基本操作的实现(合并)
    4123=喵帕斯之天才少女
    3889=神奇的函数
    1586=计算组合数
  • 原文地址:https://www.cnblogs.com/love-jelly-pig/p/9984647.html
Copyright © 2011-2022 走看看