zoukankan      html  css  js  c++  java
  • c++ vector 的坑

    一个空的vector执行pop_back操作会发生什么

    由于之前看STL源码剖析的时候,发现所执行的操作如下:

    只是简单的将末尾的finish迭代器减1后destroy。这让人产生一个疑问:假如这个vector为空了,finish=start了,finish再减1不就不在vector的内存控制范围了么。于是,我打算看一下vs2013编译器和g++编译器的源码。

    vs2013的编译器源码如下:

    #if _ITERATOR_DEBUG_LEVEL == 2
    void pop_back()
    { // erase element at end
    if (empty())
    _DEBUG_ERROR("vector empty before pop");
    else
    { // erase last element
    _Orphan_range(this->_Mylast - 1, this->_Mylast);
    this->_Getal().destroy(this->_Mylast - 1);
    --this->_Mylast;
    }
    }

    #else /* _ITERATOR_DEBUG_LEVEL == 2 */
    void pop_back()
    { // erase element at end
    this->_Getal().destroy(this->_Mylast - 1);
    --this->_Mylast;
    }
    #endif /* _ITERATOR_DEBUG_LEVEL == 2 */</span>

    该源码的意思就是,在debug模式下运行,是会检测vector是否empty的,但在release模式下不会检测。经过测试,debug下pop_back一个空的vector会报错,但release没有,但是release下面,pop_back后这个vector基本上就废了,你不能再push_back了,会报错。因为pop_back显然已经将vector的对象的结构破坏。

    在g++编译器下测试,其源码如下:

    void pop_back() _GLIBCXX_NOEXCEPT
    {
    --this->_M_impl._M_finish;
    _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
    }</span>

    结果也是一样的,pop_back一个空的vector会破坏整个vector对象,具体的作用就是finish迭代器失效。但有趣的是,下面的代码可以运行:

    vec.push_back(0);
    vec.pop_back();
    vec.pop_back();
    vec[0] = 1;
    cout << vec[0];</span>

    原因就是,push_back插入对象是 分为申请空间和构造对象两步,pop_back的destroy只有析构对象的作用,没有deallocate的回收空间的作用。所以,pop_back之后,空间是没有释放的,vec[0]能够执行赋值行为并输出,但这个时候因为finish迭代器的损毁,vec已经不能执行push_back操作了。另外提一下,destroy函数在析构对象的时候,做了进一步的判断,如果该对象存在有效的析构函数,则调用改析构函数析构之,否则什么都不做,比如int类型的对象。


    总的来说,pop_back的准确调用需要程序员来保证,在执行pop_back时候最好能预先做一些判断。

    vector clear() 方法 内存释放问题

    自己查到的三处说法的对比:
    一、转自知道的答案:https://zhidao.baidu.com/question/323662520.html?qq-pf-to=pcqq.c2c#
    vector,clear()并不真正释放内存(这是为优化效率所做的事),clear实际所做的是为vector中所保存的所有对象调用析构函数(如果有的话),然后初始化size这些东西,让觉得把所有的对象清除了。
      真正释放内存是在vector的析构函数里进行的,所以一旦超出vector的作用域(如函数返回),首先它所保存的所有对象会被析构,然后会调用allocator中的deallocate函数回收对象本身的内存。
      所以,某些编译器clear后还能访问到对象数据(因为它根本没清除),在一些比较新的C++编译器上(例如VS2008),当进行数组引用时(例如a[2]这种用法),STL库中会有一些check函数根据当前容器的size值来判断下标引用是否超出范围,如果超出,则会执行这样一句:
      _THROW(out_of_range, "invalid vector<T> subscript");
      即抛出一个越界异常,clear后没有捕获异常,程序在新编译器编译后就会崩溃掉。
    -------------------------分割线--------------------------------------------------------------
    二、转自博客:https://www.cnblogs.com/summerRQ/articles/2407974.html


    vector : C++ STL中的顺序容器,封装数组

    1. vector容器的内存自增长 

    与其他容器不同,其内存空间只会增长,不会减小。先来看看"C++ Primer"中怎么说:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。

    关于vector的内存空间,有两个函数需要注意:size()成员指当前拥有的元素个数;capacity()成员指当前(容器必须分配新存储空间之前)可以存储的元素个数。reserve()成员可以用来控制容器的预留空间。vector另外一个特性在于它的内存空间会自增长,每当vector容器不得不分配新的存储空间时,会以加倍当前容量的分配策略实现重新分配。例如,当前capacity为50,当添加第51个元素时,预留空间不够用了,vector容器会重新分配大小为100的内存空间,作为新连续存储的位置。

    2. vector内存释放

    由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

    如果需要空间动态缩小,可以考虑使用deque。如果非vector不可,可以用swap()来帮助你释放内存。具体方法如下:

    vector<int> nums;
    nums.push_back(1);
    nums.push_back(1);
    nums.push_back(2);
    nums.push_back(2);
    vector<int>().swap(nums); //或者nums.swap(vector<int> ())
    或者如下所示,使用一对大括号,意思一样的:

    //加一对大括号是可以让tmp退出{}的时候自动析构
    {
    std::vector<int> tmp = nums;
    nums.swap(tmp);
    }
     swap()是交换函数,使vector离开其自身的作用域,从而强制释放vector所占的内存空间,总而言之,释放vector内存最简单的方法是vector<int>.swap(nums)。当时如果nums是一个类的成员,不能把vector<int>.swap(nums)写进类的析构函数中,否则会导致double free or corruption (fasttop)的错误,原因可能是重复释放内存。标准解决方法如下:

    template < class T >
    void ClearVector( vector< T >& vt )
    {
    vector< T > vtTemp;
    veTemp.swap( vt );
    }
     

    3. 利用vector释放指针

    如果vector中存放的是指针,那么当vector销毁时,这些指针指向的对象不会被销毁,那么内存就不会被释放。如下面这种情况,vector中的元素时由new操作动态申请出来的对象指针:

    #include <vector>
    using namespace std;

    vector<void *> v;
    每次new之后调用v.push_back()该指针,在程序退出或者根据需要,用以下代码进行内存的释放: 

    for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++)
    if (NULL != *it)
    {
    delete *it;
    *it = NULL;
    }
    v.clear();


    三、转自博客:https://blog.csdn.net/hk_john/article/details/72463318


    最近经常用到vector容器,发现它的clear()函数有点意思,经过验证之后进行一下总结。

    clear()函数的调用方式是,vector<datatype> temp(50);//定义了50个datatype大小的空间。temp.clear();

    作用:将会清空temp中的所有元素,包括temp开辟的空间(size),但是capacity会保留,即不可以以temp[1]这种形式赋初值,只能通过temp.push_back(value)的形式赋初值。

    同样对于vector<vector<datatype> > temp1(50)这种类型的变量,使用temp1.clear()之后将会不能用temp1[1].push_back(value)进行赋初值,只能使用temp1.push_back(temp);的形式。

    下面的代码是可以运行的。

    #include <iostream>
    #include<vector>
    using namespace std;
    int main(){
    vector<vector<int>> test(50);
    vector<int> temp;
    test[10].push_back(1);
    cout<<test[10][0]<<endl;
    test.clear();
    for(int i=0;i<51;i++)
    test.push_back(temp);
    system("pause");
    return 0;
    }

    但是这样是会越界错误的。
    #include <iostream>
    #include<vector>
    using namespace std;
    int main(){
    vector<vector<int>> test(50);
    vector<int> temp;
    test[10].push_back(1);
    cout<<test[10][0]<<endl;
    test.clear();
    for(int i=0;i<50;i++)
    test[i].push_back(1);
    system("pause");
    return 0;
    }
    并且即使我们使用
    for(int i=0;i<100;i++)
    test[i].push_back(1);
    都是可以的,因为size已经被清除了。

    转自:https://blog.csdn.net/tangle001/article/details/47026989

          https://blog.csdn.net/acoolgiser/article/details/81018296

  • 相关阅读:
    WPF 关于拖拽打开文件的注意事项
    asp.net core 3.1中对Mongodb BsonDocument的序列化和反序列化支持
    用百度webuploader分片上传大文件
    多线程学习笔记
    web.config数据库连接字符串加密
    Visual Studio 2010 常用快捷方式
    Team Foundation Server 2013 日常使用使用手册(四)分支与合并
    Team Foundation Server 2013 日常使用使用手册(三)上传新工程、创建任务、创建bug、设置预警
    Team Foundation Server 2013 日常使用使用手册(二)修改、签入、撤销、回滚、对比代码变更
    Team Foundation Server 2013 日常使用使用手册(一)-本地连接TFS、查看任务
  • 原文地址:https://www.cnblogs.com/Malphite/p/11566877.html
Copyright © 2011-2022 走看看