Контейнеры STL/CLR

Библиотека STL/CLR состоит из контейнеров, которые похожи на контейнеры, найденные в стандартной библиотеке C++, но выполняются в управляемой среде платформа .NET Framework. Он не поддерживается в актуальном состоянии с фактической стандартной библиотекой C++ и поддерживается для поддержки прежних версий.

В этом документе представлен обзор контейнеров в STL/CLR, таких как требования к элементам контейнера, типы элементов, которые можно вставить в контейнеры, и проблемы владения элементами в контейнерах. При необходимости различия между собственной стандартной библиотекой C++ и STL/CLR упоминание.

Требования для элементов контейнеров

Все элементы, вставляемые в контейнеры STL/CLR, должны соответствовать определенным рекомендациям. Дополнительные сведения см. в разделе "Требования к элементам контейнера STL/CLR".

Допустимые элементы контейнера

Контейнеры STL/CLR могут содержать один из двух типов элементов:

  • Обрабатывает ссылочные типы.

  • Ссылочные типы.

  • Распаковка типов значений.

Не удается вставить типы значений в контейнеры STL/CLR.

Дескриптор ссылочных типов

Вы можете вставить дескриптор в ссылочный тип в контейнер STL/CLR. Дескриптор в C++, предназначенный для среды CLR, аналогичен указателю в собственном языке 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;
}

Типы значений без почтового ящика

Кроме того, можно вставить тип несовершитого значения в контейнер STL/CLR. Тип значения без почтового ящика — это тип значения, который не был указан в ссылочный тип.

Элемент типа значения может быть одним из стандартных типов значений, таких как int, или это может быть определяемый пользователем тип значения, например 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 .

Последствия производительности и памяти

При определении того, следует ли использовать дескрипторы для ссылок на типы или типы значений в качестве элементов контейнера. Если вы решили использовать типы значений, помните, что копия элемента выполняется каждый раз, когда элемент вставляется в контейнер. Для небольших объектов это не должно быть проблемой, но если вставляемые объекты являются большими, производительность может пострадать. Кроме того, если вы используете типы значений, невозможно хранить один элемент в нескольких контейнерах одновременно, так как каждый контейнер будет иметь собственную копию элемента.

Если вы решили использовать дескриптора для ссылок на типы, производительность может увеличиться, так как при вставке элемента в контейнер не требуется. Кроме того, в отличие от типов значений, один и тот же элемент может существовать в нескольких контейнерах. Однако если вы решите использовать дескриптор, необходимо учесть, что дескриптор действителен и что объект, на который он ссылается, не был удален в другом месте программы.

Проблемы с владением контейнерами

Контейнеры в STL/CLR работают с семантикой значений. Каждый раз при вставке элемента в контейнер вставляется копия этого элемента. Если вы хотите получить семантику, похожую на ссылку, можно вставить дескриптор в объект, а не сам объект.

При вызове метода очистки или удаления контейнера объектов дескриптора объекты, на которые ссылаются дескриптор, не освобождаются от памяти. Необходимо либо явно удалить объект, либо, так как эти объекты находятся в управляемой куче, разрешите сборщику мусора освободить память после того, как он определяет, что объект больше не используется.

См. также

Справочник по стандартной библиотеке C++