Share via


STL/CLR コンテナー

STL/CLR ライブラリは、C++ 標準ライブラリで見つかったものに似たコンテナーで構成されますが、.NET Framework のマネージド環境内で実行されます。 実際の C++ 標準ライブラリでは最新の状態に保たれず、レガシ サポートのために維持されます。

このドキュメントでは、コンテナー要素の要件、コンテナーに挿入できる要素の種類、コンテナー内の要素に関する所有権の問題など、STL/CLR のコンテナーの概要について説明します。 必要に応じて、ネイティブ C++ 標準ライブラリと STL/CLR の違いについて説明します。

コンテナー要素の要件

STL/CLR コンテナーに挿入される要素はすべて、特定のガイドラインに従う必要があります。 詳細については、「STL/CLR コンテナー要素の要件」を参照してください。

有効なコンテナー要素

STL/CLR コンテナーは、次の 2 種類の要素のいずれかを保持できます。

  • 参照型へのハンドル。

  • 参照型。

  • unboxed 値型。

ボックス化された値型を STL/CLR コンテナーに挿入することはできません。

参照型へのハンドル

STL/CLR コンテナーに参照型へのハンドルを挿入できます。 CLR を対象とする C++ のハンドルは、ネイティブ C++ のポインターに類似しています。 詳細については、「オブジェクト演算子 (^) へのハンドル」を参照してください。

次の例は、Employee オブジェクトへのハンドルを cliext::set に挿入する方法を示しています。

// cliext_container_valid_reference_handle.cpp
// compile with: /clr

#include <cliext/set>

using namespace cliext;
using namespace System;

ref class Employee
{
public:
    // STL/CLR containers might require a public constructor, so it
    // is a good idea to define one.
    Employee() :
        name(nullptr),
        employeeNumber(0) { }

    // All STL/CLR containers require a public copy constructor.
    Employee(const Employee% orig) :
        name(orig.name),
        employeeNumber(orig.employeeNumber) { }

    // All STL/CLR containers require a public assignment operator.
    Employee% operator=(const Employee% orig)
    {
        if (this != %orig)
        {
            name = orig.name;
            employeeNumber = orig.employeeNumber;
        }

        return *this;
    }

    // All STL/CLR containers require a public destructor.
    ~Employee() { }

    // Associative containers such as maps and sets
    // require a comparison operator to be defined
    // to determine proper ordering.
    bool operator<(const Employee^ rhs)
    {
        return (employeeNumber < rhs->employeeNumber);
    }

    // The employee's name.
    property String^ Name
    {
        String^ get() { return name; }
        void set(String^ value) { name = value; }
    }

    // The employee's employee number.
    property int EmployeeNumber
    {
        int get() { return employeeNumber; }
        void set(int value) { employeeNumber = value; }
    }

private:
    String^ name;
    int employeeNumber;
};

int main()
{
    // Create a new employee object.
    Employee^ empl1419 = gcnew Employee();
    empl1419->Name = L"Darin Lockert";
    empl1419->EmployeeNumber = 1419;

    // Add the employee to the set of all employees.
    set<Employee^>^ emplSet = gcnew set<Employee^>();
    emplSet->insert(empl1419);

    // List all employees of the company.
    for each (Employee^ empl in emplSet)
    {
        Console::WriteLine("Employee Number {0}: {1}",
            empl->EmployeeNumber, empl->Name);
    }

    return 0;
}

参照型

また、参照型 (参照型へのハンドルではない) を STL/CLR コンテナーに挿入することもできます。 ここでの主な違いは、参照型のコンテナーが削除された場合、そのコンテナー内のすべての要素に対してデストラクターが呼び出されることです。 参照型へのハンドルのコンテナーでは、これらの要素のデストラクターは呼び出されません。

次の例は、Employee オブジェクトを cliext::set に挿入する方法を示しています。

// cliext_container_valid_reference.cpp
// compile with: /clr

#include <cliext/set>

using namespace cliext;
using namespace System;

ref class Employee
{
public:
    // STL/CLR containers might require a public constructor, so it
    // is a good idea to define one.
    Employee() :
        name(nullptr),
        employeeNumber(0) { }

    // All STL/CLR containers require a public copy constructor.
    Employee(const Employee% orig) :
        name(orig.name),
        employeeNumber(orig.employeeNumber) { }

    // All STL/CLR containers require a public assignment operator.
    Employee% operator=(const Employee% orig)
    {
        if (this != %orig)
        {
            name = orig.name;
            employeeNumber = orig.employeeNumber;
        }

        return *this;
    }

    // All STL/CLR containers require a public destructor.
    ~Employee() { }

    // Associative containers such as maps and sets
    // require a comparison operator to be defined
    // to determine proper ordering.
    bool operator<(const Employee^ rhs)
    {
        return (employeeNumber < rhs->employeeNumber);
    }

    // The employee's name.
    property String^ Name
    {
        String^ get() { return name; }
        void set(String^ value) { name = value; }
    }

    // The employee's employee number.
    property int EmployeeNumber
    {
        int get() { return employeeNumber; }
        void set(int value) { employeeNumber = value; }
    }

private:
    String^ name;
    int employeeNumber;
};

int main()
{
    // Create a new employee object.
    Employee empl1419;
    empl1419.Name = L"Darin Lockert";
    empl1419.EmployeeNumber = 1419;

    // Add the employee to the set of all employees.
    set<Employee>^ emplSet = gcnew set<Employee>();
    emplSet->insert(empl1419);

    // List all employees of the company.
    for each (Employee^ empl in emplSet)
    {
        Console::WriteLine("Employee Number {0}: {1}",
            empl->EmployeeNumber, empl->Name);
    }

    return 0;
}

unboxed 値型

unboxed 値型を STL/CLR コンテナーに挿入することもできます。 unboxed 値型は、参照型に "ボックス化" されていない値型です。

値型要素は、int などの標準値型の 1 つにすることも、value class などのユーザー定義の値型にすることもできます。 詳細については、「クラスと構造体」を参照してください

次の例では、Employee クラスを値型にすることで、最初の例を変更します。 この値型は、最初の例と同じく cliext::set に挿入されます。

// cliext_container_valid_valuetype.cpp
// compile with: /clr

#include <cliext/set>

using namespace cliext;
using namespace System;

value class Employee
{
public:
    // Associative containers such as maps and sets
    // require a comparison operator to be defined
    // to determine proper ordering.
    bool operator<(const Employee^ rhs)
    {
        return (employeeNumber < rhs->employeeNumber);
    }

    // The employee's name.
    property String^ Name
    {
        String^ get() { return name; }
        void set(String^ value) { name = value; }
    }

    // The employee's employee number.
    property int EmployeeNumber
    {
        int get() { return employeeNumber; }
        void set(int value) { employeeNumber = value; }
    }

private:
    String^ name;
    int employeeNumber;
};

int main()
{
    // Create a new employee object.
    Employee empl1419;
    empl1419.Name = L"Darin Lockert";
    empl1419.EmployeeNumber = 1419;

    // Add the employee to the set of all employees.
    set<Employee>^ emplSet = gcnew set<Employee>();
    emplSet->insert(empl1419);

    // List all employees of the company.
    for each (Employee empl in emplSet)
    {
        Console::WriteLine("Employee Number {0}: {1}",
            empl.EmployeeNumber, empl.Name);
    }

    return 0;
}

コンテナーに値型へのハンドルを挿入しようとすると、コンパイラ エラー C3225 が生成されます。

パフォーマンスとメモリへの影響

参照型へのハンドルと値型のどちらをコンテナー要素として使用するかを決定する場合は、いくつかの要因を考慮する必要があります。 値型を使用する場合は、要素がコンテナーに挿入されるたびに、要素のコピーが作成されることに注意してください。 小さなオブジェクトの場合は問題になりませんが、挿入されるオブジェクトが大きい場合は、パフォーマンスが低下する可能性があります。 また、値型を使用している場合、各コンテナーには要素の独自のコピーが含まれるため、1 つの要素を複数のコンテナーに同時に格納することはできません。

代わりに参照型へのハンドルを使用すると、コンテナーに挿入するときに要素のコピーを作成する必要がないので、パフォーマンスが向上する可能性があります。 また、値型とは異なり、同じ要素が複数のコンテナーに存在できます。 ただし、ハンドルを使用する場合は、ハンドルが有効であり、参照するオブジェクトがプログラムの他の場所で削除されていないことを確認する必要があります。

コンテナーに関する所有権の問題

STL/CLR のコンテナーは、値セマンティクスに対して機能します。 コンテナーに要素を挿入するたびに、その要素のコピーが挿入されます。 参照に似たセマンティクスを取得する場合は、オブジェクト自体ではなく、オブジェクトへのハンドルを挿入できます。

ハンドル オブジェクトのコンテナーの clear または erase メソッドを呼び出した場合、ハンドルが参照するオブジェクトはメモリから解放されません。 オブジェクトを明示的に削除する必要があります。または、これらのオブジェクトはマネージド ヒープに存在するため、オブジェクトが使用されなくなったと判断された後にガベージ コレクターでメモリを解放できます。

関連項目

C++ 標準ライブラリ リファレンス