STL/CLRコンテナの要素は3通り
BCLコレクションは参照ハンドルもしくは値(value class)のいずれかをその要素とします。STL/CLRコンテナはそれに加え、参照そのものを要素とすることができます。
コンテナの挙動を調べるため、以下に示すref classを用意しました。
public ref class RefClass { private: int value_; public: // コンストラクタ(デフォルト) RefClass() { Console::WriteLine(L"RefClass.ctor()"); } // コンストラクタ(コピー) RefClass(const RefClass% r) : value_(r.value_) { Console::WriteLine(L"RefClass.ctor(const RefClass%)"); } RefClass(RefClass^ r) : value_(r->value_) { Console::WriteLine(L"RefClass.ctor(RefClass^)"); } // コンストラクタ(単項) RefClass(int n) : value_(n) { Console::WriteLine(L"RefClass.ctor(int)"); } // デストラクタ ~RefClass() { Console::WriteLine(L"RefClass.dtor()"); } // ファイナライザ !RefClass() { Console::WriteLine(L"RefClass.fin.()"); } // コピー演算 RefClass% operator=(const RefClass% r) { value_ = r.value_; Console::WriteLine(L"RefClass.op=(const RefClass%)"); return *this; } // プロパティ property int Value { int get() { return value_; } void set(int n) { value_ = n; } } };
STL/CLRコンテナ:vector<RefClass^>に要素を追加し、その挙動を調べてみましょう。
// STL/CLRコンテナ: vectorの場合(参照ハンドル) { Console::WriteLine("---------- vector<RefClass^>"); cliext::vector<RefClass^> container; RefClass^ rc = gcnew RefClass(); for ( int i = 0; i < 3; ++i ) { rc->Value = i; container.push_back(rc); } for each ( RefClass^ item in container ) { Console::WriteLine(item->Value); } Console::WriteLine("---------------------------"); }
---------- vector<RefClass^> RefClass.ctor() 2 2 2 --------------------------- RefClass.fin.()
要素のValueプロパティを書き変えながら挿入しているにもかかわらず、列挙された値はどれも同じになっています。コンテナが内包するのが参照ハンドルなので、内包される要素はどれもみな一つの参照を指しているからです。BCLコレクションでも同じ挙動を示します。
// BCLコレクション: List の場合 { Console::WriteLine("---------- List<RefClass^>"); List<RefClass^> container; RefClass^ rc = gcnew RefClass(); for ( int i = 0; i < 3; ++i ) { rc->Value = i; container.Add(rc); } for each ( RefClass^ item in container ) { Console::WriteLine(item->Value); } Console::WriteLine("---------------------------"); }
STL/CLRコンテナには参照ハンドルではなく、参照そのものを要素とすることができます。このときvector<RefClass>のように、要素の型にハンドルを示す ^ が付きません。
// STL/CLRコンテナ: vectorの場合(参照) { Console::WriteLine("---------- vector<RefClass>"); cliext::vector<RefClass> container; RefClass^ rc = gcnew RefClass(); for ( int i = 0; i < 3; ++i ) { rc->Value = i; container.push_back(rc); } for each ( RefClass^ item in container ) { Console::WriteLine(item->Value); } Console::WriteLine("---------------------------"); }
---------- vector<RefClass> RefClass.ctor() RefClass.ctor(RefClass^) RefClass.ctor(RefClass^) RefClass.ctor(RefClass^) 0 1 2 --------------------------- RefClass.dtor() RefClass.dtor() RefClass.dtor() RefClass.fin.()
参照を要素とした場合、要素の挿入時にコピー・コンストラクタでコピーが生成され、そのコピーがコンテナ内に格納されています。
STL/CLRコンテナはBCL interfaceを実装している
冒頭にも述べたように、STL/CLRコンテナはBCLのコレクションinterfaceを実装しています。これによってアセンブリの外からinterfaceを介してアクセスが可能です。
STL/CLRコンテナのBCL-interface実装はprivate部に隠されているので直接触ることができませんが、interfaceにキャストすることで例えばvector<T>をIList<T>として扱うことができます。
#include <cliext/vector> using namespace System; using namespace System::Collections::Generic; int main() { cliext::vector<String^> sv; IList<String^>^ ils = %sv; // BCL-interfaceにキャスト ils->Add(L"zero"); // 末尾に追加 ils->Add(L"two"); ils->Add(L"THREE"); ils->Insert(1,L"one"); // 挿入 ils[3] = L"three"; // インデクサで3番目を更新 ils->RemoveAt(2); // 削除 // IEnumerable<T> を実装するので for each で列挙可能 for each ( String^ item in sv ) { Console::WriteLine(item); } ils->Clear(); // 全要素削除 Console::WriteLine(L"要素数: {0}", ils->Count); }
zero one three 要素数: 0
