C++ vector swap()去除多餘容量(詳解版)

2020-07-16 10:05:25
《如何避免vector容器進行不必要的擴容》一節中,遺留了一個問題,即如何借助 swap() 成員方法去除 vector 容器中多餘的容量?本節將就此問題給讀者做詳細的講解。

我們知道,在使用 vector 容器的過程中,其容器會根據需要自行擴增。比如,使用 push_back()、insert()、emplace() 等成員方法向 vector 容器中新增新元素時,如果當前容器已滿(即 size() == capacity()),則它會自行擴容以滿足新增新元素的需求。當然,還可以呼叫 reserve() 成員方法來手動提升當前 vector 容器的容量。

舉個例子(程式一):
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int>myvector;
    cout << "1、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    //利用 myvector 容器儲存 10 個元素
    for (int i = 1; i <= 10; i++) {
        myvector.push_back(i);
    }
    cout << "2、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    //手動為 myvector 擴容
    myvector.reserve(1000);
    cout << "3、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    return 0;
}
程式執行結果為:

1、當前 myvector 擁有 0 個元素,容量為 0
2、當前 myvector 擁有 10 個元素,容量為 13
3、當前 myvector 擁有 10 個元素,容量為 1000


除了可以新增元素外,vector 模板類中還提供了 pop_back()、erase()、clear() 等成員方法,可以輕鬆實現刪除容器中已儲存的元素。但需要注意得是,借助這些成員方法只能刪除指定的元素,容器的容量並不會因此而改變。

例如在程式一的基礎上,末尾(return 0 之前)新增如下語句:
myvector.erase(myvector.begin());
cout << "4、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;

myvector.pop_back();
cout << "5、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;

myvector.clear();
cout << "6、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
此段程式碼的執行結果為:

4、當前 myvector 擁有 9 個元素,容量為 1000
5、當前 myvector 擁有 8 個元素,容量為 1000
6、當前 myvector 擁有 0 個元素,容量為 1000

顯然,myvector 容器儲存的元素個數在減少,但容量並不會減小。

幸運的是,myvector 模板類中提供有一個 shrink_to_fit() 成員方法,該方法的功能是將當前 vector 容器的容量縮減至和實際儲存元素的個數相等。例如,在程式一的基礎上,新增如下語句:
myvector.shrink_to_fit();
cout << "7、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
該語句的執行結果為:

7、當前 myvector 擁有 10 個元素,容量為 10

顯然,myvector 容器的容量由 1000 縮減到了 10。

利用swap()方法去除vector多餘容量

除此之外,vector 模板類中還提供有 swap() 成員方法,該方法的基礎功能是交換 2 個相同型別的 vector 容器(交換容量和儲存的所有元素),但其也能用於去除 vector 容器多餘的容量。

如果想用 swap() 成員方法去除當前 vector 容器多餘的容量時,可以套用如下的語法格式:

vector<T>(x).swap(x);

其中,x 指當前要操作的容器,T 為該容器儲存元素的型別。

下面程式演示了此語法格式的 swap() 方法的用法和功能:
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int>myvector;
    //手動為 myvector 擴容
    myvector.reserve(1000);
    cout << "1、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    //利用 myvector 容器儲存 10 個元素
    for (int i = 1; i <= 10; i++) {
        myvector.push_back(i);
    }
    //將 myvector 容量縮減至 10
    vector<int>(myvector).swap(myvector);
    cout << "2、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    return 0;
}
程式執行結果為:

1、當前 myvector 擁有 0 個元素,容量為 1000
2、當前 myvector 擁有 10 個元素,容量為 10

顯然,第 16 行程式碼成功將 myvector 容器的容量 1000 修改為 10,此行程式碼的執行流程可細分為以下 3 步:

1) 先執行 vector<int>(myvector),此表示式會呼叫 vector 模板類中的拷貝建構函式,從而建立出一個臨時的 vector 容器(後續稱其為 tempvector)。

值得一提的是,tempvector 臨時容器並不為空,因為我們將 myvector 作為引數傳遞給了複製建構函式,該函數會將 myvector 容器中的所有元素拷貝一份,並儲存到 tempvector 臨時容器中。

注意,vector 模板類中的拷貝建構函式只會為拷貝的元素分配儲存空間。換句話說,tempvector 臨時容器中沒有空閒的儲存空間,其容量等於儲存元素的個數。


2) 然後藉助 swap() 成員方法對 tempvector 臨時容器和 myvector 容器進行調換,此過程不僅會交換 2 個容器儲存的元素,還會交換它們的容量。換句話說經過 swap() 操作,myvetor 容器具有了 tempvector 臨時容器儲存的所有元素和容量,同時 tempvector 也具有了原 myvector 容器儲存的所有元素和容量。

3) 當整條語句執行結束時,臨時的 tempvector 容器會被銷毀,其占據的儲存空間都會被釋放。注意,這裡釋放的其實是原 myvector 容器佔用的儲存空間。

經過以上 3 個步驟,就成功的將 myvector 容器的容量由 100 縮減至 10。

利用swap()方法清空vector容器

在以上內容的學習過程中,如果讀者善於舉一反三,應該不難想到,swap() 方法還可以用來清空 vector 容器。

當 swap() 成員方法用於清空 vector 容器時,可以套用如下的語法格式:

vector<T>().swap(x);

其中,x 指當前要操作的容器,T 為該容器儲存元素的型別。

注意,和上面語法格式唯一的不同之處在於,這裡沒有為 vector<T>() 表示式傳遞任何引數。這意味著,此表示式將呼叫 vector 模板類的預設建構函式,而不再是複製建構函式。也就是說,此格式會先生成一個空的 vector 容器,再藉助 swap() 方法將空容器交換給 x,從而達到清空 x 的目的。

下面程式演示了此語法格式的 swap() 方法的用法和功能:
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int>myvector;
    //手動為 myvector 擴容
    myvector.reserve(1000);
    cout << "1、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    //利用 myvector 容器儲存 10 個元素
    for (int i = 1; i <= 10; i++) {
        myvector.push_back(i);
    }
    //清空 myvector 容器
    vector<int>().swap(myvector);
    cout << "2、當前 myvector 擁有 " << myvector.size() << " 個元素,容量為 " << myvector.capacity() << endl;
    return 0;
}
程式執行結果為:

1、當前 myvector 擁有 0 個元素,容量為 1000
2、當前 myvector 擁有 0 個元素,容量為 0