Vector && String

自古美人都是妖i 提交于 2020-01-30 15:21:11

    在需要动态分配数组时,考虑使用vector或string代替数组。大部分情况下,vector或string都可以完全替代Array。但当有性能要求时,基于引用计数实现的string则有可能无法满足要求。多线程环境下,基于引用计数实现的string存在性能隐忧,考虑禁用引用基数或者采用vector<char>代替。
   
    利用reserve(size_t n)减少频繁reallocation开销。
    对于Vector而言:

1        vector<int> v;
2        v.reserve(1000);
3        forint i = 0; i < 1000++i )
4            v.push_back(i);

    当然,这种方法只能在可以确切的或大概预计出需要多大容量的情况下使用。但如果事先无法预计,则可预先分配尽可能大的空间来容纳所需的元素。当添加完所有元素后,采用"Swap trick"裁减掉多余的空间 (Shrink-to-fit: reduction in capacity)。
   
    Swap Trick:

1        vector<int> v;
2        
3        vector<int>(v).swap(v);

    vector<int>(v)创建一个临时vector,该vector包含了v中所有int元素。创建临时vector调用vector的拷贝构造函数,而且仅拷贝v中实际包含的int元素的个数。因此,临时vector的容量恰好是容纳所有元素所需的空间。之后再通过swap(...)交换两个容器中的内容。交换过后,v的容量就是实际的int元素的容量,v中多余的空间被裁减掉。而该句执行完后,临时vector被释放。string与vector类似。
    同样可以利用"Swap Trick"清空vector或string:        

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

    与resize(...)不同,"swap trick"用于修改vector或string的capacity,使其capacity=size。
   
       
    避免使用vector<bool>。vector<bool>是一个伪容器,为了节省空间,vector<bool>并不包含实际的bools。详见Item18(《Effecitve STL》)
    string有多种实现需要在使用前了解当前string的实现的方式及其特点。《Effective STL》总了这些实现中6种不同的地方:
        1> string是否采用引用计数
        2> string对象的大小有可能1倍到至少7倍于char*指针的大小
        3> 创建一个新的string值时,可能需要0、1或2次动态分配
        4> 若多个string的值相同,则在这些string对象之间,它们的大小和容量信息未必共享
        5> string是否支持pre-object allocators
        6> 不同实现在考虑最小分配字符缓冲上采取不同的策略
       
    要把vector和string的数据传递给遗留APIs,对于vector而言,使用&v[0]。   

1        // for vector
2        void doSomething(const int* pInts, size_t numInts);
3        
4        if(!v.empty()) {
5            doSomething(&v[0], v.size();    // Never replace &v[0] by v.begin();
6        }

    对于string而言,不能采用类似vector的方法来得到指向容器数据的指针。原因:1> 存储在string中的数据不是一定是连续的;2> string的内部表示不保证数据以null字符结尾。因此采用s.c_str(),c_str()返回string中数据的拷贝,只读的

1        // for string
2        void doSomething(const char* pStrings);
3        
4        doSomething(s.c_str());

    在legacy APIs中修改s.c_str()的值。对于多数情况下,修改vector的值是没有问题的。但当该vector本身是有序,API修改vector的值时,则会出现潜在的问题,尤其是当API试图在vector中增加元素时。
    只有vector允许通过legacy API初始化,但前提是需要知道该API初始化vector时所需的最大容量。

 1        // C API: this function takes a pointer to an array of at most arraySize
 2        // doubles and writes data to it. It returns the number of doubles written,
 3        // which is never more than maxNumDoubles.
 4        size_t fillArray(double *pArray, size_t arraySize);
 5        vector<double> vd(maxNumDoubles); // create a vector whose
 6                                          // size is maxNumDoubles
 7        vd.resize(fillArray(&vd[0], vd.size())); // have fillArray write data
 8                                                 // into vd, then resize vd
 9                                                 // to the number of
10                                                 // elements fillArray wrote

    如果需要通过legacy API初始化其他容器,则可以利用vector作为过渡:

 1        // For string
 2        size_t fillArray(double *pArray, size_t arraySize);
 3        vector<double> vd(maxNumDoubles); 
 4        size_t charsWritten = fillString(&vc[0], vc.size());        
 5        string s(vc.begin(), vc.begin()+charsWritten);   
 6        
 7        // For other containers
 8        size_t fillArray(double *pArray, size_t arraySize);
 9        vector<double> vd(maxNumDoubles);    
10        vd.resize(fillArray(&vd[0], vd.size()));
11            
12        deque<double> d(vd.begin(), vd.end());
13        list<double> l(vd.begin(), vd.end());
14        set<double> s(vd.begin(), vd.end());

    同样,可以用类似初始化的方法把其他容器中保存的元素传递给legacy APIs

1        set<int> intSets;
2        
3        vector<int> v(intSets.begin(), intSets.end());
4        if(!v.empty())
5            doSomething(&v[0], v.size());

参考:《Effective STL》

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!