Coleções (C++/CX)

Em um programa do C++/CX, você pode usar livremente os contêineres STL (Biblioteca de Modelos Padrão) ou qualquer outro tipo de coleção definida pelo usuário. No entanto, quando você passa coleções de e para a ABI (Interface binária de aplicativo) do Windows Runtime – por exemplo, a um controle XAML ou a um cliente JavaScript – deve usar os tipos de coleção do Windows Runtime.

O Windows Runtime define as interfaces para coleções e tipos relacionados e o C++/CX fornece as implementações C++ concretas no arquivo de cabeçalho collection.h. Esta ilustração mostra as relações entre os tipos de coleção:

Diagrama da árvore de herança do C++ CX para tipos de coleções.

Uso do vetor

Quando a sua classe precisar transmitir um contêiner de sequência para outro componente do Windows Runtime, use Windows::Foundation::Collections:: IVector<T> como parâmetro ou tipo de retorno e Platform::Collections::Vector<T> como a implementação concreta. Se você tentar usar um tipo Vector em um valor ou parâmetro de retorno público, será gerado o erro do compilador C3986. Você pode corrigir o erro alterando Vector para IVector.

Importante

Se você estiver passando uma sequência no seu próprio programa, use Vector ou std::vector , pois eles são mais eficientes do que IVector. Use IVector somente ao passar o contêiner pela ABI.

O sistema de tipos do Windows Runtime não oferece suporte ao conceito de matrizes denteadas e, portanto, você não pode passar um IVector<Platform::Array<T>> como um valor de retorno ou parâmetro de método. Para passar uma matriz denteada ou uma sequência de sequências através da ABI, use IVector<IVector<T>^>.

Vector<T> fornece os métodos necessários para adição, remoção e acesso a itens na coleção e é implicitamente conversível em IVector<T>. Você também pode usar algoritmos STL em instâncias de Vector<T>. O exemplo a seguir demonstra alguns usos básicos. As funções begin e end aqui são do namespace Platform::Collections , não do namespace std .

#include <collection.h>
#include <algorithm>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;


void Class1::Test()
{
    Vector<int>^ vec = ref new Vector<int>();
    vec->Append(1);
    vec->Append(2);
    vec->Append(3);
    vec->Append(4);
    vec->Append(5);


    auto it = 
        std::find(begin(vec), end(vec), 3);

    int j = *it; //j = 3
    int k = *(it + 1); //or it[1]

    // Find a specified value.
    unsigned int n;         
    bool found = vec->IndexOf(4, &n); //n = 3

    // Get the value at the specified index.
    n = vec->GetAt(4); // n = 3

    // Insert an item.
    // vec = 0, 1, 2, 3, 4, 5
    vec->InsertAt(0, 0);

    // Modify an item.
    // vec = 0, 1, 2, 12, 4, 5,
    vec->SetAt(3, 12);

    // Remove an item.
    //vec = 1, 2, 12, 4, 5 
    vec->RemoveAt(0);

    // vec = 1, 2, 12, 4
    vec->RemoveAtEnd();

    // Get a read-only view into the vector.
    IVectorView<int>^ view = vec->GetView();
}

Se houver um código existente que use std::vector e você quiser reutilizá-lo em um componente do Windows Runtime, basta usar um dos construtores Vector que adota um std::vector ou um par de iteradores para construir um Vector no ponto em que você passa a coleção pela ABI. O exemplo a seguir mostra como usar o construtor de movimentação Vector para inicialização eficiente de um std::vector. Após a operação de movimentação, a variável de vec original não será mais válida.

//#include <collection.h>
//#include <vector>
//#include <utility> //for std::move
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
//using namespace std;
IVector<int>^ Class1::GetInts()
{
    vector<int> vec;
    for(int i = 0; i < 10; i++)
    {
        vec.push_back(i);
    }    
    // Implicit conversion to IVector
    return ref new Vector<int>(std::move(vec));
}

Se você tiver um vetor de cadeias de caracteres que deve passar pela ABI em algum ponto futuro, será preciso decidir se as cadeias de caracteres serão criadas inicialmente como tipos de std::wstring ou como tipos de Platform::String^ . Se for necessário muito processamento nessas cadeias de caracteres, use wstring. Caso contrário, crie as cadeias de caracteres como tipos de Platform::String^ e evite o custo de convertê-las posteriormente. Você também deve decidir se essas cadeias de caracteres serão colocadas em um std:vector ou Platform::Collections::Vector internamente. Como prática geral, use std::vector e crie um Platform::Vector a partir dele somente quando passar o contêiner pela ABI.

Tipos de valor no vetor

Qualquer elemento a ser armazenado em um Platform::Collections::Vector deve oferecer suporte à comparação de igualdade, seja implicitamente ou usando um comparador personalizado std::equal_to que você fornece. Todos os tipos de referência e todos os tipos escalares oferecem suporte implicitamente às comparações de igualdade. Para os tipos de valor não escalar, como Windows::Foundation::DateTime, ou para comparações personalizadas (por exemplo, objA->UniqueID == objB->UniqueID), é necessário fornecer um objeto de função personalizado.

Elementos VectorProxy

Platform::Collections::VectorIterator e Platform::Collections::VectorViewIterator permitem o uso de loops e algoritmos range for como std::sort com um contêiner IVector<T>. Porém, os elementos IVector não podem ser acessados por meio do desreferenciamento do ponteiro C++; eles podem ser acessados somente por pelos métodos GetAt e SetAt . Portanto, esses iteradores usam as classes proxy Platform::Details::VectorProxy<T> e Platform::Details::ArrowProxy<T> para oferecer acesso aos elementos individuais por meio dos operadores *, -> e [], conforme exigido pelo STL. Estritamente falando, dado um IVector<Person^> vec, o tipo de *begin(vec) será VectorProxy<Person^>. Entretanto, o objeto proxy é quase sempre transparente ao seu código. Esses objetos proxy não estão documentados porque são apenas para uso interno dos iteradores, mas é útil saber como o mecanismo funciona.

Quando você usar um loop for baseado em intervalo sobre contêineres IVector, use auto&& para habilitar a variável de iterador para associar corretamente aos elementos VectorProxy. Se você usar auto&, o aviso do compilador C4239 será gerado e VectoryProxy será mencionado no texto de aviso.

A ilustração a seguir mostra um loop range for sobre um IVector<Person^>. Observe que a execução é interrompida no ponto de interrupção na linha 64. A janela QuickWatch mostra que a variável de iterador p é na verdade um VectorProxy<Person^> que tem as variáveis de membro m_v e m_i . Entretanto, quando você chama GetType nessa variável, é retornado o tipo idêntico à instância Person de p2. A conclusão é que, embora VectorProxy e ArrowProxy possam aparecer em QuickWatch, determinados erros do compilador do depurador, ou outros locais, geralmente não precisam ter uma codificação explícita para eles.

Captura de tela da depuração do VectorProxy em um intervalo baseado em loop.

Um cenário em que é necessário codificar em torno do objeto proxy é quando você precisa executar um dynamic_cast nos elementos — por exemplo, quando você está procurando objetos XAML de um determinado tipo em uma coleção de elementos UIElement . Nesse caso, você deve primeiro converter o elemento em Platform::Object^ e depois executar a conversão dinâmica:

void FindButton(UIElementCollection^ col)
{
    // Use auto&& to avoid warning C4239
    for (auto&& elem : col)
    {
        Button^ temp = dynamic_cast<Button^>(static_cast<Object^>(elem));
        if (nullptr != temp)
        {
            // Use temp...
        }
    }
}

Uso do mapa

Este exemplo mostra como inserir itens e consultá-los em um Platform::Collections::Map, e depois retornar Map como um tipo Windows::Foundation::Collections::IMapView somente leitura.

//#include <collection.h>
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
IMapView<String^, int>^ Class1::MapTest()
{
    Map<String^, int>^ m = ref new Map<String^, int >();
    m->Insert("Mike", 0);
    m->Insert("Dave", 1);
    m->Insert("Doug", 2);
    m->Insert("Nikki", 3);
    m->Insert("Kayley", 4);
    m->Insert("Alex", 5);
    m->Insert("Spencer", 6);

   // PC::Map does not support [] operator
   int i = m->Lookup("Doug");
   
   return m->GetView();
   
}

Em geral, para a funcionalidade de mapa interna, prefira o tipo std::map por motivos de desempenho. Se precisar transmitir o contêiner pela ABI, construa um Platform::Collections::Map a partir de std::map e retorne Map como um Windows::Foundation::Collections::IMap. Se você tentar usar um tipo Map em um valor ou parâmetro de retorno público, será gerado o erro do compilador C3986. Você pode corrigir o erro alterando Map para IMap. Em alguns casos (por exemplo, se você não estiver fazendo um grande número de pesquisas ou inserções e estiver passando a coleção pela ABI frequentemente), poderá ser menos caro usar Platform::Collections::Map desde o início e evitar o custo de converter o std::map. Em qualquer caso, evite as operações de pesquisa e inserção em um IMap , pois elas são as menos eficazes dos três tipos. Converta em IMap somente no ponto em que você passa o contêiner pela ABI.

Tipos de valor no mapa

Os elementos de um Platform::Collections::Map são ordenados. Qualquer elemento a ser armazenado em um Map deve oferecer suporte à comparação de inferioridade com ordenação fraca estrita, seja implicitamente ou usando um comparador personalizado stl::less fornecido por você. Os tipos escalares oferecem suporte à comparação implicitamente. Para os tipos de valor não escalar, como Windows::Foundation::DateTime, ou para comparações personalizadas (por exemplo, objA->UniqueID < objB->UniqueID), você deverá fornecer um comparador personalizado.

Tipos de coleção

As coleções se enquadram em quatro categorias: versões modificáveis e somente leitura das coleções de sequência e coleções associativas. Além disso, o C++/CX aprimora as coleções fornecendo três classes de iterador que simplificam o acesso de coleções.

Os elementos de uma coleção modificável podem ser alterados, mas os elementos de uma coleção somente leitura, que é conhecida como exibição, só podem ser lidos. Os elementos de uma coleção Platform::Collections::Vector ou Platform::Collections::VectorView podem ser acessados com o uso de um iterador ou o Vector::GetAt da coleção e um índice. Os elementos de uma coleção associativa podem ser acessados usando Map::Lookup da coleção e uma chave.

Classe Platform::Collections::Map
uma coleção modificável, associativa. Os elementos de mapa são pares chave-valor. Há suporte para as operações de pesquisar uma chave para recuperar seu valor associado e iterar por todos os pares chave-valor.

Map e MapView se tornam modelos no <K, V, C = std::less<K>>; portanto, você pode personalizar o comparador. Além disso, Vector e VectorView se tornam modelos no <T, E = std::equal_to<T>> para que você possa personalizar o comportamento de IndexOf(). Isso é importante principalmente para Vector e VectorView e estruturas de valor. Por exemplo, para criar um Vector<Windows::Foundation::DateTime>, você deve fornecer um comparador personalizado, pois DateTime não sobrecarrega o operador ==.

Classe Platform::Collections::MapView
Uma versão somente leitura de Map.

Classe Platform::Collections::Vector
Uma coleção de sequências modificável. Vector<T> oferece suporte a operações Append de acesso aleatório de tempo constante e de tempo constante amortizado.

Classe Platform::Collections::VectorView
Uma versão somente leitura de Vector.

Classe Platform::Collections::InputIterator
Um iterador STL que satisfaz os requisitos de um iterador de entrada STL.

Classe Platform::Collections::VectorIterator
Um iterador STL que satisfaz os requisitos de um iterador de acesso aleatório mutável STL.

Classe Platform::Collections::VectorViewIterator
Um iterador STL que satisfaz os requisitos de um iterador de acesso aleatório STL const.

Funções begin() e end()

Para simplificar o uso de STL para processar Vector, VectorView, Map, MapView e objetos Windows::Foundation::Collections arbitrários, o C++/CX oferece suporte a sobrecargas de funções não membro begin Function e end Function.

A tabela a seguir lista os iteradores e as funções disponíveis.

Iterators Funções
Platform::Collections::VectorIterator<T>

(Armazena internamente Windows::Foundation::Collections:: IVector<T> e int.)
begin/ end(Windows::Foundation::Collections:: IVector<T>)
Platform::Collections::VectorViewIterator<T>

(Armazena internamente IVectorView<T>^ e int.)
begin/ end (IVectorView<T>^)
Platform::Collections::InputIterator<T>

(Armazena internamente IIterator<T>^ e T.)
begin/ end (IIterable<T>)
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Armazena internamente IIterator<T>^ e T.)
begin/ end (IMap<K,V>.
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Armazena internamente IIterator<T>^ e T.)
begin/ end (Windows::Foundation::Collections::IMapView)

Eventos de alteração de coleção

Vector e Map oferecem suporte à associação de dados em coleções XAML implementando eventos que ocorrem quando um objeto de coleção é alterado ou redefinido, ou quando um elemento de uma coleção é inserido, removido ou alterado. Você pode escrever seus próprios tipos que ofereçam suporte à associação de dados, embora eles não possam ser herdados de Map nem de Vector , pois esses tipos são lacrados.

Os delegados Windows::Foundation::Collections::VectorChangedEventHandler e Windows::Foundation::Collections::MapChangedEventHandler especificam as assinaturas dos manipuladores de eventos para eventos de alteração da coleção. A classe enum pública Windows::Foundation::Collections::CollectionChange e as classes ref Platform::Collection::Details::MapChangedEventArgs e Platform::Collections::Details::VectorChangedEventArgs armazenam os argumentos de eventos para determinar o que provocou o evento. Os tipos *EventArgs são definidos no namespace Details, pois você não precisa construí-los nem consumi-los explicitamente quando usa Map ou Vector.

Confira também

Sistema de tipos
Referência da linguagem C++/CX
Referência de namespaces