vectorで確保した領域の切り詰め(swap技法)

http://www.geocities.jp/ky_webid/cpp/library/002.html

既に確保した領域を減らしたい場合どうすればよいのかというお話。

reserve関数で領域の確保ができるので、これで減らすことができるか確認してみます。

なお、

vector の容量については、「現在、実際に使っている分」と「とりあえず一括で確保してある分」という2つの見方があることを理解しておくべきです。前者は size() で、後者は capacity() で取得できます。両者の差分が「要素を追加しても、領域の拡張が起こらない個数」ということになります。

ということなのでcapacity関数を使えば、現在確保してある分のサイズを取得できるのでこれで確認してみます。

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

int main () {
    vector<int> array;
    array.reserve(1000);
    
    // 1000と表示
    cout << array.capacity() << endl;
    
    // 100に切り詰め?
    array.reserve(100);
    
    // 1000と表示
    cout << array.capacity() << endl;
    
    return 0;
}
$ main
1000
1000

だめですね、確保された領域自体は減っていません。

ではどうすれば切り詰めることができるのか?

swap技法というものを使います。

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

int main () {
    vector<int> array;
    array.reserve(1000);
    
    // 1000と表示
    cout << array.capacity() << endl;
    
    {
        vector<int> temp(100);
        array.swap(temp);
    }
    
    // 100と表示
    cout << array.capacity() << endl;
    
    return 0;
}
$ main
1000
100

うまくいってますね。

ただしあくまでもswapは入れ替えなので中身のデータも入れ替わってしまうので実質元のデータの中身はクリアされることになります。

これでは結局新しく変数宣言してるのと変わらないですよね。

そこで中身のデータは残したまま領域の切りつめを方法として名前のないオブジェクトを利用するという方法があります。

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

int main () {
    vector<int> array;
    array.reserve(1000);
    
    // 100まで要素を使う
    for(int i=0;i<100;++i) {
        array.push_back(i);
    }
    
    // 1000と表示
    cout << array.capacity() << endl;
    
    {
        // 名前のないオブジェクトのコンストラクタにarrayを渡す
        vector<int> (array).swap(array);
    }
    
    // 100と表示
    cout << array.capacity() << endl;
    
    return 0;
}

これで中身のデータを保持しつつ切りつめができました。

ちなみにこの名前のないオブジェクトについてはまだよくわかってないのでまた調べる必要がありますね。