Contenedores de STL/CLR
La biblioteca STL/CLR consta de contenedores similares a los que se encuentran en la biblioteca estándar de C++, pero se ejecuta en el entorno administrado de .NET Framework. No se mantiene actualizada con la biblioteca estándar de C++ real. Se conserva con fines de compatibilidad heredada.
En este documento se proporciona información general sobre los contenedores de STL/CLR, como los requisitos de los elementos de contenedor, los tipos de elementos que se pueden insertar en los contenedores y los problemas de propiedad con los elementos de los contenedores. Si procede, se mencionan las diferencias entre la biblioteca estándar nativa de C++ y STL/CLR.
Requisitos para los elementos de contenedor
Todos los elementos insertados en contenedores de STL/CLR deben cumplir ciertas directrices. Para obtener más información, consulte Requisitos de los elementos de contenedor de STL/CLR.
Elementos de contenedor válidos
Los contenedores de STL/CLR pueden tener uno de dos tipos de elementos:
Identificadores de tipos de referencia.
Tipos de referencia.
Tipos de valor con conversión unboxing.
No se pueden insertar tipos de valor con conversión boxing en ninguno de los contenedores de STL/CLR.
Identificadores de tipos de referencia
Se puede insertar un identificador de un tipo de referencia en un contenedor de STL/CLR. Un identificador en C++ que tiene como destino CLR es análogo a un puntero en C++ nativo. Para obtener más información, consulte Operador de identificador de objeto (^).
Ejemplo
En el ejemplo siguiente, se muestra cómo insertar un identificador de un objeto Employee en un elemento 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;
}
Tipos de referencia
También se puede insertar un tipo de referencia (en lugar de un identificador de un tipo de referencia) en un contenedor de STL/CLR. La principal diferencia aquí es que, cuando se elimina un contenedor de tipos de referencia, se llama al destructor para todos los elementos de ese contenedor. En un contenedor de identificadores de tipos de referencia, no se llamaría a los destructores de estos elementos.
Ejemplo
En el ejemplo siguiente, se muestra cómo insertar un objeto Employee en un elemento 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;
}
Tipos de valor con conversión unboxing
También se puede insertar un tipo de valor con conversión unboxing en un contenedor de STL/CLR. Un tipo de valor con conversión unboxing es un tipo de valor que no se ha convertido (mediante conversión boxing) en un tipo de referencia.
Un elemento de tipo de valor puede ser uno de los tipos de valor estándar, como int
, o bien puede ser un tipo de valor definido por el usuario, como value class
. Para obtener más información, consulte ref class y ref struct (C++/CLI y C++/CX).
Ejemplo
En el ejemplo siguiente, se modifica el primer ejemplo haciendo que la clase Employee sea un tipo de valor. A continuación, este tipo de valor se inserta en un elemento cliext::set
igual que en el primer ejemplo.
// 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;
}
Si intenta insertar un identificador de un tipo de valor en un contenedor, se genera el error del compilador C3225.
Implicaciones en el rendimiento y la memoria
Debe tener en cuenta varios factores a la hora de determinar si deben usarse identificadores de tipos de referencia o tipos de valor como elementos de contenedor. Si decide usar tipos de valor, recuerde que se realiza una copia de un elemento cada vez que se inserta en el contenedor. En el caso de los objetos pequeños, esto no debería ser un problema, pero si los objetos que se insertan son grandes, podría afectar al rendimiento. Además, si usa tipos de valor, es imposible almacenar un elemento en varios contenedores al mismo tiempo, porque cada contenedor tendría su propia copia del elemento.
Si decide usar identificadores de tipos de referencia en su lugar, el rendimiento podría aumentar, porque no es necesario realizar una copia del elemento cuando se inserta en el contenedor. Además, a diferencia de los tipos de valor, el mismo elemento puede existir en varios contenedores. Sin embargo, si decide usar identificadores, debe tener cuidado y asegurarse de que el identificador es válido y de que el objeto al que hace referencia no se ha eliminado en otra parte del programa.
Problemas de propiedad con contenedores
Los contenedores de STL/CLR funcionan con la semántica de valores. Cada vez que se inserta un elemento en un contenedor, se inserta una copia de ese elemento. Si desea obtener una semántica similar a la de referencias, puede insertar un identificador de un objeto en lugar del propio objeto.
Cuando se llama a los métodos clear o erase de un contenedor de objetos de identificador, los objetos a los que hacen referencia los identificadores no se quitan de la memoria. Debe eliminar explícitamente el objeto o, ya que estos objetos residen en el montón administrado, permitir que el recolector de elementos no utilizados libere la memoria una vez que determine que el objeto ya no se está usando.