STL學習

xiboliha發表於2024-10-27

1.順序容器

順序容器是將單一型別元素聚集起來成為容器,然後根據位置來儲存和訪問這些元素。標準庫常用順序容器如下:

  • vector,可變大小陣列。支援快速隨機訪問,在尾部之外的位置插入或刪除元可能很慢。
  • deque,雙端佇列。支援快速隨機訪問,在頭尾部插入速度很快。
  • list,雙向連結串列。只支援雙向順序訪問,在list中任何位置插入刪除都很快。
  • forward_list,單向連結串列。只支援單向順序訪問,在連結串列任何位置插入刪除都很快。

容器只定義了少量操作,大多數額外操作則有演算法庫提供。容器型別的操作集合具有以下層次結構特點:一些操作適用於所有容器型別;另外一些操作則只適用於順序或關聯容器型別;還有一些操作只適用於順序或關聯容器型別的一個子集。

1.1 順序容器定義與初始化

所有的容器都是類模版,要定義某種特殊的容器,必須在容器後的尖括號內提供存放元素的資料型別。容器元素型別必須滿足以下兩個約束:元素型別必須支援賦值運算;元素型別的物件必須可以複製。所有容器型別都定義了預設建構函式,用於建立指定型別的空容器物件。除了預設建構函式,容器型別還提供其他的建構函式,使程式設計師可以指定元素初值。在C++11中,我們可以對容器進行列表初始化。

vector<string> svec;  //預設建構函式
vector<string> svec2(svec);  //將一個容器複製給另一個容器時,型別必須匹配
list<string> slist(svec.begin(), svec.end());   //初始化為一段元素的副本

const list<int>::size_type list_size = 64; 
list<string> slist(list_size, "eh");  //分配和初始化指定數目的元素

list<string> authors = {"Qin","Li"};  //C++11列表初始化

1.2 順序容器的常用操作

1.2.1 新增元素

53611-7530a110b1b41226.png
新增元素的方法.png
vector<string> container;
string text_word; 
while (cin >> text_word) 
     container.push_back(text_word); 

list<int> ilist; 
for (size_t ix = 0; ix != 4; ++ix) 
     ilist.push_front(ix); 

任何insert或push操作都可能導致迭代器失效。當編寫迴圈將元素插入到vector或deque容器中時,程式必須確保迭代器在每次迴圈後都得到更新。為了避免儲存end迭代器,可以在每次做完插入運算後重新計算end迭代器值:

vector<int>::iterator first = v.begin();//不要令last = v.end();
while (first != v.end()) 
{ 
     first = v.insert(first, 42); // insert new value 
     ++first; // advance first just past the element we added 
} 

1.2.2 新增元素

53611-7530a110b1b41226.png
新增元素的方法.png
vector<string> container;
string text_word; 
while (cin >> text_word) 
     container.push_back(text_word); 

list<int> ilist; 
for (size_t ix = 0; ix != 4; ++ix) 
     ilist.push_front(ix); 

任何 insert 或 push 操作都可能導致迭代器失效。當編寫迴圈將元素插入到 vector 或 deque 容器中時,程式必須確保迭代器在每次迴圈後都得到更新。為了避免儲存 end 迭代器,可以在每次做完插入運算後重新計算 end 迭代器值:

vector<int>::iterator first = v.begin();//不要令last = v.end();
while (first != v.end()) 
{ 
     first = v.insert(first, 42); // insert new value 
     ++first; // advance first just past the element we added 
} 

1.2.3 順序容器大小的操作

為避免每次新增元素都會執行記憶體分配和釋放的操作,vector和string每次獲取新的記憶體空間時,都會分配比需求更大的空間作為備用,以此減少記憶體分配的次數。

vector<int> c(10,2);
int a = c.size();  //返回容器c中的元素個數
bool b = c.empty();  //判斷容器是否為空

c.reserve(20)  //分配至少能容納20個元素的記憶體空間
c.capacity()  //返回容器可以容納的元素個數

c.resize(15)  //

1.2.4 刪除元素

53611-3ec234dfe170a12a.png
刪除順序容器內元素的操作.png

pop_front 和 pop_back 函式的返回值並不是刪除的元素值,而是 void。要獲取刪除的元素值,則必須在刪除元素之前呼叫 front 或 back 函式。

while (!ilist.empty()) { 
         process(ilist.front()); // do something with the current top of ilist 
         ilist.pop_front();      // done; remove first element 
     } 

erase 操作不會檢查它的引數,因此必須確保用作引數的迭代器或迭代器範圍是有效的。通常,程式設計師必須在容器中找出要刪除的元素後,才使用 erase 操作。尋找一個指定元素的最簡單方法是使用標準庫的 find 演算法。

string searchValue("Quasimodo"); 
list<string>::iterator iter = find(slist.begin(), slist.end(), searchValue); 
if (iter != slist.end())    slist.erase(iter); 

1.3 容器介面卡

標準庫來提供了三種容器介面卡。實際上,介面卡是根據原始的容器型別所提供的操作,通過定義新的操作介面,來適應基礎的容器型別。順序容器介面卡包括stack、queue和priority_queue型別。

棧的常用操作:

  • empty,判斷棧是否為空,為空則返回true。
  • size,返回棧中元素個數。
  • pop,刪除棧頂元素,但不返回其值。
  • top,返回棧頂元素的值。
  • push,在棧頂壓入新元素。

佇列的常用操作:

  • empty,size 同棧
  • pop,刪除隊首元素,但不返回其值。
  • push,在隊尾壓入一個新元素。
  • front,返回隊首元素。
  • back,返回隊尾元素。

2. 關聯容器

關聯容器支援通過鍵來高效地查詢和讀取元素,兩個基本的關聯容器型別是map和set。map的元素以鍵-值(key-value)對的形式組織:鍵用於元素在map中的索引,而值則表示所儲存和讀取的資料。set僅包含一個鍵,並有效地支援關於某個鍵是否存在的查詢。map可理解為字典,set可理解為一類元素的集合。

關聯容器和順序容器的本質差別在於:關聯容器通過鍵(key)儲存和讀取元素,而順序容器則通過元素在容器中的位置順序儲存和訪問元素。

set 和 map 型別的物件所包含的元素都具有不同的鍵,不允許為同一個鍵新增第二個元素。如果一個鍵必須對應多個例項,則需使用 multimap 或 multi set,這兩種型別允許多個元素擁有相同的鍵。

2.1 pair型別

pair 包含兩個資料值。在建立 pair 物件時,必須提供兩個型別名:pair 物件所包含的兩個資料成員各自對應的型別名字。如果在建立 pair 物件時不提供初始化式,則呼叫預設建構函式對其成員採用值初始化。

pair <string, int> word_count;
typedef pair<string, string> Author;   //利用 typedef 簡化其宣告
Author joyce("James", "Joyce");

與其他標準庫型別不同,對於 pair 類,可以直接訪問其資料成員:其成員都是僅有的,分別命名為 first 和 second。只需使用普通的點操作符(成員訪問標誌)即可訪問其成員:

string firstBook; 
if (author.first == "James" && author.second == "Joyce") 
    firstBook = "Stephen Hero"; 

除了建構函式,標準庫還定義了一個 make_pair 函式,由傳遞給它的兩個實參生成一個新的 pair 物件。可如下使用該函式建立新的 pair 物件,並賦給已存在的 pair 物件:

pair<string, string> next_auth; 
string first, last; 
while (cin >> first >> last) { 
    next_auth = make_pair(first, last); 
} 

2.2 map型別

map 物件的元素是鍵-值對,也即每個元素包含兩個部分:鍵以及由鍵關聯的值。map 的 value_type 就反映了這個事實。該型別比前面介紹的容器所使用的元素型別要複雜得多:value_type 是儲存元素的鍵以及值的 pair 型別,而且鍵為 const。例如,word_count 陣列的 value_type 為 pair<const string, int> 型別。

map <string,int> word_count;
word_count.insert( make_pair("James", "Joyce") );
map<string, int>::iterator map_it = word_count.begin(); 
 cout << map_it->first; 
 cout << " " << map_it->second;

2.2.1 給map新增元素

map容器中新增鍵-值元素對,可使用 insert 成員實現;或者,先用下標操作符獲取元素,然後給獲取的元素賦值。在這兩種情況下,一個給定的鍵只能對應於一個元素這一事實影響了這些操作的行為。

用下標操作符來獲取該鍵所關聯的值。如果該鍵已在容器中,則返回該鍵所關聯的值。只有在所查詢的鍵不存在時,map 容器才為該鍵建立一個新的元素,並將它插入到此 map 物件中。此時,所關聯的值採用值初始化:類型別的元素用預設建構函式初始化,而內建型別的元素初始化為 0。

map<string, int> word_count; // empty map from string to int 
string word; 
while (cin >> word) 
    ++word_count[word]; 

使用下標給 map 容器新增新元素時,元素的值部分將採用值初始化。通常,我們會立即為其賦值,其實就是對同一個物件進行初始化並賦值。而插入元素的另一個方法是:直接使用 insert 成員,其語法更緊湊:

word_count.insert(map<string, int>::value_type("Anna", 1)); 
word_count.insert(make_pair("Anna", 1)); 

map 物件中一個給定鍵只對應一個元素。如果試圖插入的元素所對應的鍵已在容器中,則 insert 將不做任何操作。但是,帶有一個鍵-值 pair 形參的 insert 版本將返回一個值:包含一個迭代器和一個 bool 值的 pair 物件,其中迭代器指向 map 中具有相應鍵的元素,而 bool 值則表示是否插入了該元素。如果該鍵已在容器中,則其關聯的值保持不變,返回的 bool 值為 true。在這兩種情況下,迭代器都將指向具有給定鍵的元素。

map<string, int> word_count; 
string word; 
while (cin >> word) { 
    // inserts element with key equal to word and value 1; 
    // if word already in word_count, insert does nothing 
    pair<map<string, int>::iterator, bool> ret =  
         word_count.insert(make_pair(word, 1)); 
    if (!ret.second)          // word already in word_count 
        ++ret.first->second;  // increment counter 
} 

2.2.2 查詢並讀取map中的元素

不能使用下標來查詢map中的某一元素是否存在,因為如果該鍵不在 map 容器中,那麼下標操作會插入一個具有該鍵的新元素。map 容器提供了兩個操作:count 和 find,用於檢查某個鍵是否存在而不會插入該鍵。

對於 map 物件,count 成員的返回值只能是 0 或 1。map 容器只允許一個鍵對應一個例項,所以 count 可有效地表明一個鍵是否存在。find 操作返回指向元素的迭代器,如果元素不存在,則返回 end 迭代器。

int occurs = 0;
map <string, int>::iterator it = word_count.find("foobar");
if(it != wor_count.end() )
    occurs = it->second;

2.2.3 從map物件中刪除元素

53611-4385de1ebe69a34d.png
從 map 物件中刪除元素
if (word_count.erase(removal_word)) 
    cout << "ok: " << removal_word << " removed\n"; 
else cout << "oops: " << removal_word << " not found!\n"; 

2.2.4 map物件的迭代遍歷

與其他容器一樣,map 同樣提供 begin 和 end 運算,以生成用於遍歷整個容器的迭代器。例如,可如下將 map 容器 word_count 的內容輸出:

map<string, int>::const_iterator it = word_count.begin();
while(it != word_count.end()){
    cout<< it->first <<" occurs "
            << it->second << " times " <<endl;
    ++it;
}

2.3 set型別

map 容器是鍵-值對的集合,好比以人名為鍵的地址和電話號碼。相反地,set 容器只是單純的鍵的集合。例如,某公司可能定義了一個名為 bad_checks 的 set 容器,用於記錄曾經給本公司發空頭支票的客戶。當只想知道一個值是否存在時,使用 set 容器是最適合的。例如,在接收一張支票前,該公司可能想查詢 bad_checks 物件,看看該客戶的名字是否存在。

set 容器支援大部分的 map 操作,包括上面描述的建構函式、 insert 操作、 count 和 find 操作、 erase 操作等。但是, 不支援下標操作符,而且沒有定義 mapped_type 型別。在 set 容器中,value_type 不是 pair 型別,而是與 key_type 相同的型別。它們指的都是 set 中儲存的元素型別。這一差別也體現了 set 儲存的元素僅僅是鍵,而沒有所關聯的值。與 map 一樣,set 容器儲存的鍵也必須唯一,而且不能修改

vector<int> ivec;
for( vector<int>::size_type i = 0; i !=10; ++i){
    ivec.push_back(i);
    ivec.push_back(i);
}
set<int> iset(ivec.begin(), ivec.end());
cout << ivec.size() << endl;      // prints 20 
cout << iset.size() << endl;      // prints 10 

set<string> set1;
set1.insert("the");

set<int> iset2;
iset2. insert( ivec.begin(), ivec.end() );     // iset2 has 10 elements

iset.find(1);     // returns iterator that refers to the element with key == 1
iset.find(11);   // returns iterator == iset.end() 
 iset.count(1);    // returns 1 

set<int>::iterator set_it = isec.find(1);
cout<< *set_it <<endl;

正如不能修改 map 中元素的鍵部分一樣,set 中的鍵也為 const。在獲得指向 set 中某元素的迭代器後,只能對其做讀操作,而不能做寫操作。

2.4 multimap 和 multiset 型別

map 和 set 容器中,一個鍵只能對應一個例項。而 multiset 和 multimap 型別則允許一個鍵對應多個例項。例如,在電話簿中,每個人可能有單獨的電話號碼列表。在作者的文章集中,每位作者可能有單獨的文章標題列表。multimap 和 multiset 型別與相應的單元素版本具有相同的標頭檔案定義:分別是 map 和 set 標頭檔案。

multimap 和 multiset 所支援的操作分別與 map 和 set 的操作相同,只有一個例外:multimap 不支援下標運算。不能對 multimap 物件使用下標操作,因為在這類容器中,某個鍵可能對應多個值。為了順應一個鍵可以對應多個值這一性質,map 和 multimap,或 set 和 multiset 中相同的操作都以不同的方式做出了一定的修改。在使用 multimap 或 multiset 時,對於某個鍵,必須做好處理多個值的準備,而非只有單一的值。

由於鍵不要求是唯一的,因此每次呼叫 insert 總會新增一個元素。

帶有一個鍵引數的 erase 版本將刪除擁有該鍵的所有元素,並返回刪除元素的個數。而帶有一個或一對迭代器引數的版本只刪除指定的元素,並返回 void 型別。

關聯容器 map 和 set 的元素是按順序儲存的, multimap 和 multset 也一樣。因此,在 multimap 和 multiset 容器中,如果某個鍵對應多個例項,則這些例項在容器中將相鄰存放。 在 multimap 和 multiset 中查詢元素有三種策略,而且三種策略都基於一個事實——在 multimap 中,同一個鍵所關聯的元素必然相鄰存放。

  • 使用 find 和 count 操作
  • lower_bound 和 upper_bound
  • enual_range 函式
53611-8f1ce74c79e913d3.png
返回迭代器的關聯容器操作

equal_range 函式返回儲存一對迭代器的 pair 物件。如果該值存在,則 pair 物件中的第一個迭代器指向該鍵關聯的第一個例項,第二個迭代器指向該鍵關聯的最後一個例項的下一位置。如果找不到匹配的元素,則 pair 物件中的兩個迭代器都將指向此鍵應該插入的位置。

pair<authors_it, authors_it> pos = authors.equal_range(search_item); 
while (pos.first != pos.second) { 
    cout << pos.first->second << endl; 
    ++pos.first; 
} 

3. 常用泛型演算法

標準庫為容器型別定義的操作很少,並沒有為每個容器實現更多的操作。因為這部分操作可以抽象出來為所有的容器工作,那就是泛型演算法。所謂“泛型”是指這些演算法可以應用於多種容器型別上,而容器內的元素型別也可以多樣化。標準庫提供了100多個泛型演算法,主要定義於標頭檔案<algorithm>中,還有一組泛化的算術演算法定義於標頭檔案<numeric>中。

大多數泛型演算法是工作於容器的一對迭代器所標識的範圍,並完全通過迭代器來實現其功能。這段由迭代器指定的範圍稱為“輸入範圍”。帶有輸入範圍引數的演算法總是使用前兩個引數標記該範圍,分別指向要處理的第一個元素和最後一個元素的下一個位置。

3.1 查詢

find 和 count 演算法在輸入範圍中查詢指定值。find 演算法返回引用第一個匹配元素的迭代器,count 演算法返回元素在輸入序列中出現次數的計數。

find(beg, end, val) 
count(beg, end, val) 

在輸入範圍中查詢等於 val 的元素,使用基礎型別的相等(==)操作符。find 返回第一個匹配元素的迭代器,如果不存在在匹配元素就返回 end。count 返回 val 出現次數的計數。

find函式的原始碼如下:

template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& val)
{
    while(first != last){
        if(*first == val)    return first;
        ++first;
    }
    return last;
}

一個例子,查詢陣列中的某個值。由於指標就像內建陣列上的迭代器一樣,因此可以用find在陣列中查詢值。使用begin和end函式可以獲取指向陣列首尾元素的指標。

#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int myints[]={ 10, 20, 30, 40 };
    int* p;
    p=find(begin(myints), end(myints), 30);
    if(p!=myints+4)
        cout<<*p<<endl;
}

3.2 排序

c++中最經常使用的演算法應該就是排序演算法,也就是sort函式。當然還有partial_sort以及stable_sort。sort函式排序預設是從小到大,如果想給自定義型別排序,可以過載運算子或者自定義比較函式。

升序:sort(begin,end,less<data-type>());
降序:sort(begin,end,greater<data-type>()).

#include <algorithm>
#include <iostream>
using namespace std;

int main()
{
    int a[10]={2,4,1,23,5,76,0,43,24,65},i;
    for(i=0;i<10;i++)   cout<<a[i]<<" ";
    cout<<endl;
    sort(a,a+10,greater<int>());
    for(i=0;i<10;i++)   cout<<a[i]<<" ";
    cout<<endl;
}

當然,也可以自己寫比較函式

#include <iostream>  
#include <cstring>  
#include <algorithm>  
using namespace std;  
  
struct Data  
{  
    char data[100];   
}str[100];  
  
bool cmp(const Data &elem1, const Data &elem2)  
{  
    if (strcmp(elem1.data, elem2.data) < 0)  
        return true;  
    return false;  
}  
  
int main()  
{  
    int n, i;  
    while (cin>>n)  
    {  
        for (i=0; i<n; ++i)  
        {  
            cin>>str[i].data;  
        }  
          
        sort(str, str+n, cmp);  
          
        for (i=0; i<n; ++i)  
            cout<<str[i].data<<endl;  
    }  
    return 0;  
}  

3.3 去重unique

unique的作用是從輸入序列中刪除”所有相鄰的重複元素。該演算法刪除相鄰的重複元素,然後重新排列輸入範圍內的元素,並且返回一個迭代器(容器的長度沒變,只是元素順序改變了),表示無重複的值範圍的結束。

sort(words.begin(), words.end());   //排序
vector<string>::iterator end_unique =  unique(words.begin(), words.end());  //去重
words.erase(end_unique, words.end());  //刪除結尾元素

在STL中unique函式是一個去重函式, unique的功能是去除相鄰的重複元素(只保留一個),其實它並不真正把重複的元素刪除,是把重複的元素移到後面去了,然後依然儲存到了原陣列中,然後 返回去重後最後一個元素的地址,因為unique去除的是相鄰的重複元素,所以一般用之前都會要排一下序。

原始碼如下:

template <class ForwardIterator>
ForwardIterator unique (ForwardIterator first, ForwardIterator last)
{
    if (first==last) return last;
    ForwardIterator result = first;
    while (++first != last){
        if (!(*result == *first))    *(++result)=*first;
    }
    return ++result;
}

3.4 賦值fill

fill函式可以可以向容器當中的一定範圍能賦值,一共接受3個引數,類似於memset函式。
fill(beg, end, val)

4. Stirng常用操作

4.1 通過下標操作字串:

//交換當前字串與s2的值
void swap(string &s2);

//刪除pos開始的n個字元,返回修改後的字串
string &erase(int pos = 0, int n = npos);

//從pos開始查詢字元c在當前字串的位置
int find(char c, int pos = 0) const;

//從pos開始查詢字串s在當前串中的位置
int find(const char *s, int pos = 0) const;

//刪除從p0開始的n0個字元,然後在p0處插入串s
string &replace(int p0, int n0,const char *s);

//刪除從p0開始的n0個字元,然後在p0處插入串s
string &replace(int p0, int n0,const string &s);

string &insert(int p0, const char *s);
string &insert(int p0, const char *s, int n);
string &insert(int p0,const string &s);

一個示例

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s = "asdfghjklsdsdfgh";
    cout<<s.size()<<endl;
    cout<<s.find('s')<<endl;
    cout<<s.find('s',s.find('s')+1)<<endl;
    
    //將"sd"替換成"sb"
    int i = 0;
    while(i < s.size()){
        int pos = s.find("sd",i);
        if(pos == string::npos)    break;
        s.replace(pos,2,"mb");
        cout<<s<<endl;
        i = pos + 2;
    }
    
    string s2 = s; 
    s2.erase(0,3);
    cout<<s2<<" end"<<endl; 
}

4.2 通過迭代器操作字串

//刪除[first,last)之間的所有字元,返回刪除後迭代器的位置
iterator erase(iterator first, iterator last);
//刪除it指向的字元,返回刪除後迭代器的位置
iterator erase(iterator it);

5. 深度探索vector

5.1 insert的實現

在插入元素之前,需要判斷vector的備用空間大小是否大於插入元素個數,如果足夠則在原序列進行插入操作;如果不夠,則需要重新申請記憶體,將原資料和新增資料移動到新記憶體中(先是原資料插入點之前的資料移動到新記憶體中,然後是待插入資料,最後移動插入點之後的資料),然後析構元記憶體中的物件,最後再釋放原記憶體。

////////////////////////////////////////////////////////////////////////////////
// 在指定位置插入n個元素
////////////////////////////////////////////////////////////////////////////////
//             insert(iterator position, size_type n, const T& x)
//                                   |
//                                   |---------------- 插入元素個數是否為0?
//                                   ↓
//              -----------------------------------------
//        No    |                                       | Yes
//              |                                       |
//              |                                       ↓
//              |                                    return;
//              |----------- 記憶體是否足夠?
//              |
//      -------------------------------------------------
//  Yes |                                               | No
//      |                                               |
//      |------ (finish - position) > n?                |
//      |       分別調整指標                              |
//      ↓                                               |
//    ----------------------------                      |
// No |                          | Yes                  |
//    |                          |                      |
//    ↓                          ↓                      |
// 插入操作, 調整指標           插入操作, 調整指標          |
//                                                      ↓
//            data_allocator::allocate(len);
//            new_finish = uninitialized_copy(start, position, new_start);
//            new_finish = uninitialized_fill_n(new_finish, n, x);
//            new_finish = uninitialized_copy(position, finish, new_finish);
//            destroy(start, finish);
//            deallocate();
////////////////////////////////////////////////////////////////////////////////

template <class T, class Alloc>
void insert(iterator position, size_type n, const T& x)
{
    // 如果n為0則不進行任何操作
    if (n != 0)
    {
        if (size_type(end_of_storage - finish) >= n)
        {      // 剩下的備用空間大於等於“新增元素的個數”
            T x_copy = x;
            // 以下計算插入點之後的現有元素個數
            const size_type elems_after = finish - position;
            iterator old_finish = finish;
            if (elems_after > n)
            {
                // 插入點之後的現有元素個數 大於 新增元素個數
                uninitialized_copy(finish - n, finish, finish);
                finish += n;    // 將vector 尾端標記後移
                copy_backward(position, old_finish - n, old_finish);
                fill(position, position + n, x_copy); // 從插入點開始填入新值
            }
            else
            {
                // 插入點之後的現有元素個數 小於等於 新增元素個數
                uninitialized_fill_n(finish, n - elems_after, x_copy);
                finish += n - elems_after;
                uninitialized_copy(position, old_finish, finish);
                finish += elems_after;
                fill(position, old_finish, x_copy);
            }
        }
        else
        {   // 剩下的備用空間小於“新增元素個數”(那就必須配置額外的記憶體)
            // 首先決定新長度:就長度的兩倍 , 或舊長度+新增元素個數
            const size_type old_size = size();
            const size_type len = old_size + max(old_size, n);
            // 以下配置新的vector空間
            iterator new_start = data_allocator::allocate(len);
            iterator new_finish = new_start;
            __STL_TRY
            {
                // 以下首先將舊的vector的插入點之前的元素複製到新空間
                new_finish = uninitialized_copy(start, position, new_start);
                // 以下再將新增元素(初值皆為n)填入新空間
                new_finish = uninitialized_fill_n(new_finish, n, x);
                // 以下再將舊vector的插入點之後的元素複製到新空間
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
#         ifdef  __STL_USE_EXCEPTIONS
            catch(...)
            {
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
#         endif /* __STL_USE_EXCEPTIONS */
            destroy(start, finish);
            deallocate();
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }
}