STL コンテナの自動メモリ管理

C++ 文字列から C 文字列へ、および std::vector から C 配列への大きな利点の 1 つは、両方の C++ コンテナーが自動的にメモリを管理することです。もちろん、それは標準テンプレート ライブラリのその他のすべてのコンテナにも当てはまります。この投稿では、std::vector と std::string の自動メモリ管理について詳しく見ていきます。

ユーザーの観点からは、C++11 の std::string は std::vector のように感じられます。それが単純な理由です。それらを並行して提示できます。したがって、std::string と std::vector が C++ で最も重要なコンテナーであることは非常によく当てはまります。

std::string および std::vector

C++ ランタイムは、std::string と std::vector のサイズをその要素数に自動的に調整します。双方向で C++11 を使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// stringAndVector.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
void showInfo(const T& t,const std::string& name){

 std::cout << name << " t.size(): " << t.size() << std::endl;
 std::cout << name << " t.capacity(): " << t.capacity() << std::endl;

}

int main(){
 
 std::cout << std::endl;

 std::string str;
 std::vector<int> vec;
 
 std::cout << "Maximal size: " << std::endl;
 std::cout << "str.max_size(): " << str.max_size() << std::endl;
 std::cout << "vec.max_size(): " << vec.max_size() << std::endl;
 std::cout << std::endl;
 
 std::cout << "Empty string and vector: " << std::endl;
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;
 
 
 std::cout << "Initialized with five values: " << std::endl;
 str= {"12345"};
 vec= {1,2,3,4,5};
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;
 
 std::cout << "Added four additional values: " << std::endl;
 str += "6789";
 vec.insert(vec.end(),{6,7,8,9});
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;
 
 
 std::cout << "Resized to 30 values: " << std::endl;
 str.resize(30);
 vec.resize(30);
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;

 std::cout << "Reserved space for at least 1000 values: " << std::endl;
 str.reserve(1000);
 vec.reserve(1000);
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;
 
 std::cout << "Shrinked to the current size: " << std::endl;
 str.shrink_to_fit();
 vec.shrink_to_fit();
 showInfo(str,"String");
 showInfo(vec,"Vector");
 std::cout << std::endl;

}

プログラムは非常に簡単に取得できます。それが私の最初の考えでした。しかし、ちょっと待ってください。

入力を省くために、小さな関数 showInfo を書きました (7 ~ 13 行目)。この関数は、コンテナーのサイズ (10 行目) と容量 (11 行目) を返します。コンテナーのサイズはその要素の数であり、コンテナーの容量はコンテナーが追加の割り当てなしで保持できる要素の数です。したがって、コンテナの容量は、少なくともそのサイズと同じ大きさでなければなりません。 resize メソッドを使用してコンテナーのサイズを調整できます (49 行目と 50 行目)。メソッド予約でコンテナーの容量を調整できます (56 行目と 57 行目)。

しかし、プログラムを上から下に戻します。 19 行目と 20 行目で空の文字列と空のベクトルを作成します。その後、プログラムは文字列またはベクトルが持つことができる要素の最大数を表示します。両方のコンテナーでの各操作の後、サイズと容量が返されます。これは、コンテナーの初期化 (34 行目と 35 行目)、4 つの新しい要素の追加 (42 行目と 43 行目)、コンテナーの 30 要素へのサイズ変更 (49 行目と 50 行目)、および追加のメモリの予約に当てはまります。少なくとも 1000 要素の場合 (56 行目と 57 行目)。 C++11 では、shrink_to_fit メソッド (63 行目と 64 行目) を使用してコンテナーの容量をそのサイズに縮小できます。

Linux と Windows でのプログラムの出力を提示する前に、いくつかの観察をさせてください。

<オール>
  • コンテナのサイズと容量の調整は自動的に行われます。 new や delete などのメモリ操作は一切使用していません。
  • std::string と std::vector は同じインターフェースをサポートします。この規則の唯一の例外は 41 行目です。ここでは、C 文字列を C++ 文字列に追加しています。
  • メソッド cont.resize(n) を使用すると、n> cont.size() が true の場合、コンテナー cont はデフォルトで初期化された新しい要素を取得します。
  • メソッド cont.reserve(n) を使用すると、n> cont.capacity() が true の場合、コンテナー cont は少なくとも n 要素の新しいメモリを取得します。
  • shrink_to_fit の呼び出しは拘束力がありません。つまり、C++ ランタイムはコンテナーの容量をそのサイズに合わせて調整する必要はありません。しかし、GCC、clang、または cl.exe で shrink_to_fit メソッドを使用すると、常に不要なメモリが解放されました。
  • これがプログラムの出力です。

    私のちょっとした驚き

    このプログラムは、MSVC 15 STL 実装が GCC 4.8 STL 実装よりも少し貪欲であることを示しています。これは、特に std::string に当てはまります。したがって、空の std::string には 15 個の要素があります。しかし、Linux では std::string の最大サイズが std::vector の最大サイズと同じくらい大きいという事実に、さらに驚きました。 Linux と Windows では int が char の 4 倍の大きさであるため、これは驚くべきことです。

    #include <iostream>
    
    int main(){
     
     std::cout << std::endl;
    
     std::cout << "sizeof(char): " << sizeof(char) << std::endl;
     std::cout << "sizeof(int): " << sizeof(int) << std::endl;
     
     std::cout << std::endl;
    
    }
    

    両方の値を最大値として解釈する必要があります。この不一致について何か考えはありますか?

    私の驚きは消えました

    Mark Abraham と Clément Gregoire の助けにより、なぞは解決されました。

    Microsoft の実装はより貪欲です

    Microsoft Visuals の std::string 実装は、内部的に小さな文字列の最適化を使用します。したがって、小さな文字列はヒープ割り当てを必要とせず、スタックに直接移動します。境界はちょうど 15 文字です。 GCC は 5.1 に準拠した文字列実装を取得します。しかし、テストでは GCC 4.8 を使用しました。

    準拠した std::string 実装を使用した GCC

    準拠した std::string 実装で GCC 5.3 を使用すると、状況は異なります。

    GCC 5.3 で適合する std::string 実装を使用するには、コンパイラ フラグ -D_GLIBCXX_USE_CXX11_ABI=1 を使用する必要があります。現在、std::string の最大サイズは std::vector の最大サイズの 2 倍です。 C++11 標準では、max_size() について次のように述べています。「可能な最大文字列のサイズ。 "今、私は元気です。

    次は?

    次の投稿で、std::array を詳しく見ていきます。 std::array は、2 つの世界から最高のものを組み合わせます。一方では、std::array は C 配列と同じくらい軽量です。一方、std::array は std::vector と同じインターフェイスをサポートします。