Existe algum uso para unique_ptr com array?

std::unique_ptr tem suporte para matrizes, por exemplo:

 std::unique_ptr p(new int[10]); 

mas é necessário? provavelmente é mais conveniente usar std::vector ou std::array .

Você acha algum uso para essa construção?

Algumas pessoas não têm o luxo de usar std::vector , mesmo com alocadores. Algumas pessoas precisam de um array de tamanhos dynamics, então std::array está fora. E algumas pessoas obtêm suas matrizes de outro código que é conhecido por retornar uma matriz; e esse código não será reescrito para retornar um vector ou algo assim.

Permitindo unique_ptr , você atende a essas necessidades.

Em resumo, você usa unique_ptr quando precisa . Quando as alternativas simplesmente não vão funcionar para você. É uma ferramenta de último recurso.

Existem compensações e você escolhe a solução que corresponde ao que você deseja. Em cima da minha cabeça:

Tamanho inicial

  • vector e unique_ptr permitem que o tamanho seja especificado em tempo de execução
  • array só permite que o tamanho seja especificado em tempo de compilation

Redimensionamento

  • array e unique_ptr não permitem redimensionamento
  • vector faz

Armazenamento

  • vector e unique_ptr armazenam os dados fora do object (geralmente no heap)
  • array armazena os dados diretamente no object

Copiando

  • array e vector permitem copiar
  • unique_ptr não permite copiar

Trocar / Mover

  • vector e unique_ptr tem O (1) swap tempo e move operações
  • array tem operações de swap e movimentação de tempo O (n), onde n é o número de elementos na matriz

Anulação de ponteiro / referência / iterador

  • array garante que os pointers, referências e iteradores nunca sejam invalidados enquanto o object estiver ativo, mesmo em swap()
  • unique_ptr não possui iteradores; pointers e referências só são invalidados por swap() enquanto o object está ativo. (Depois da troca, os pointers apontam para o array com o qual você trocou, então eles ainda são “válidos” nesse sentido.)
  • vector pode invalidar pointers, referências e iteradores em qualquer realocação (e fornece algumas garantias de que a realocação só pode acontecer em certas operações).

Compatibilidade com conceitos e algoritmos

  • array e vector são ambos Contêineres
  • unique_ptr não é um contêiner

Eu tenho que admitir, isso parece uma oportunidade para alguma refatoração com design baseado em políticas.

Uma razão pela qual você pode usar um unique_ptr é se você não deseja pagar o custo de tempo de execução do valor inicializando o array.

 std::vector vec(1000000); // allocates AND value-initializes 1000000 chars std::unique_ptr p(new char[1000000]); // allocates storage for 1000000 chars 

O construtor std::vector e std::vector::resize() inicializam o valor T – mas o new não fará isso se T for um POD.

Consulte objects de valor inicializado no C ++ 11 e construtor std :: vector

Note que vector::reserve não é uma alternativa aqui: Está acessando o ponteiro bruto após std :: vector :: reserve safe?

É a mesma razão pela qual um programador C pode escolher malloc sobre calloc .

Um std::vector pode ser copiado, enquanto unique_ptr permite expressar a propriedade exclusiva do array. std::array , por outro lado, requer que o tamanho seja determinado em tempo de compilation, o que pode ser impossível em algumas situações.

Scott Meyers tem isto a dizer em C ++ Moderna Eficaz

A existência de std::unique_ptr para matrizes deve ser apenas de interesse intelectual para você, porque std::array , std::vector , std::string são praticamente sempre melhores opções de estrutura de dados do que matrizes brutas. Sobre a única situação que posso conceber quando um std::unique_ptr faria sentido seria quando você está usando uma API semelhante a C que retorna um ponteiro bruto para uma matriz de heap que você assume a propriedade.

Eu acho que a resposta de Charles Salvia é relevante: std::unique_ptr é a única maneira de inicializar uma matriz vazia cujo tamanho não é conhecido em tempo de compilation. O que Scott Meyers teria a dizer sobre essa motivação para usar std::unique_ptr ?

Ao contrário de std::vector e std::array , std::unique_ptr pode possuir um ponteiro NULL.
Isso é útil quando se trabalha com APIs C que esperam uma matriz ou NULL:

 void legacy_func(const int *array_or_null); void some_func() { std::unique_ptr ptr; if (some_condition) { ptr.reset(new int[10]); } legacy_func(ptr.get()); } 

Um padrão comum pode ser encontrado em algumas chamadas de API do Windows Win32 , em que o uso de std::unique_ptr pode ser útil, por exemplo, quando você não sabe exatamente o tamanho de um buffer de saída ao chamar alguns API do Win32 (que irá gravar alguns dados dentro desse buffer):

 // Buffer dynamically allocated by the caller, and filled by some Win32 API function. // (Allocation will be made inside the 'while' loop below.) std::unique_ptr buffer; // Buffer length, in bytes. // Initialize with some initial length that you expect to succeed at the first API call. UINT32 bufferLength = /* ... */; LONG returnCode = ERROR_INSUFFICIENT_BUFFER; while (returnCode == ERROR_INSUFFICIENT_BUFFER) { // Allocate buffer of specified length buffer.reset( BYTE[bufferLength] ); // // Or, in C++14, could use make_unique() instead, eg // // buffer = std::make_unique(bufferLength); // // // Call some Win32 API. // // If the size of the buffer (stored in 'bufferLength') is not big enough, // the API will return ERROR_INSUFFICIENT_BUFFER, and the required size // in the [in, out] parameter 'bufferLength'. // In that case, there will be another try in the next loop iteration // (with the allocation of a bigger buffer). // // Else, we'll exit the while loop body, and there will be either a failure // different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful // and the required information will be available in the buffer. // returnCode = ::SomeApiCall(inParam1, inParam2, inParam3, &bufferLength, // size of output buffer buffer.get(), // output buffer pointer &outParam1, &outParam2); } if (Failed(returnCode)) { // Handle failure, or throw exception, etc. ... } // All right! // Do some processing with the returned information... ... 

Eu usei unique_ptr para implementar um conjunto de memorys pré-alocadas usadas em um mecanismo de jogo. A idéia é fornecer pools de memory pré-alocados usados ​​em vez de alocações dinâmicas para retornar resultados de solicitações de colisão e outras coisas como física de partículas sem precisar alocar / liberar memory em cada quadro. É bastante conveniente para esse tipo de cenário, onde você precisa de pools de memory para alocar objects com tempo de vida limitado (normalmente um, dois ou três frameworks) que não exigem lógica de destruição (apenas desalocação de memory).

Resumindo: é de longe o mais eficiente em termos de memory.

Um std::string vem com um ponteiro, um comprimento e um buffer de “otimização de seqüência curta”. Mas minha situação é que eu preciso armazenar uma string que está quase sempre vazia, em uma estrutura que eu tenho centenas de milhares de caracteres. Em C, eu usaria apenas o char * , e seria nulo a maior parte do tempo. O que também funciona para C ++, exceto que um char * não possui um destruidor e não sabe se excluir. Em contraste, um std::unique_ptr será deletado quando sair do escopo. Um std::string vazio ocupa 32 bytes, mas um std::unique_ptr vazio ocupa 8 bytes, ou seja, exatamente o tamanho de seu ponteiro.

A maior desvantagem é que toda vez que eu quero saber o tamanho da string, eu tenho que chamar o strlen .

Eu enfrentei um caso onde eu tive que usar std::unique_ptr , que estava na biblioteca HDF5 (Uma biblioteca para armazenamento eficiente de dados binários, muito usada em ciência). Alguns compiladores (Visual Studio 2015 no meu caso) fornecem compression de std::vector (usando 8 bools em cada byte), o que é uma catástrofe para algo como HDF5, que não se importa com essa compression. Com std::vector , o HDF5 acabou lendo lixo devido a essa compression.

Adivinha quem estava lá para o resgate, em um caso em que o std::vector não funcionava, e eu precisava alocar um array dynamic de forma limpa? 🙂

Eles podem ser a resposta mais certa possível quando você só pode cutucar um único ponteiro por meio de uma API existente (mensagem de janela de reflection ou parâmetros de retorno de chamada relacionados à segmentação) que têm alguma duração após serem “capturados” no outro lado da hachura. mas que não está relacionado ao código de chamada:

 unique_ptr data = get_some_data(); threadpool->post_work([](void* param) { do_a_thing(unique_ptr((byte*)param)); }, data.release()); 

Todos nós queremos que as coisas sejam boas para nós. C ++ é para as outras vezes.

  • Você precisa da sua estrutura para conter apenas um ponteiro por razões de compatibilidade binária.
  • Você precisa fazer interface com uma API que retorna a memory alocada com o new[]
  • Sua empresa ou projeto tem uma regra geral contra o uso do std::vector , por exemplo, para evitar que programadores descuidados acidentalmente introduzam cópias
  • Você deseja impedir que programadores descuidados acidentalmente introduzam cópias nesta instância.

Há uma regra geral de que os contêineres C ++ devem ser preferidos ao invés de usar seus pointers. É uma regra geral; tem exceções. Tem mais; estes são apenas exemplos.

Para responder às pessoas que pensam que você “tem que” usar vector vez de unique_ptr Eu tenho um caso na programação CUDA na GPU quando você aloca memory no dispositivo você deve ir para uma matriz de ponteiro (com cudaMalloc ). Então, ao recuperar esses dados no Host, você deve ir novamente para um ponteiro e unique_ptr é bom para manipular o ponteiro facilmente. O custo extra da conversão de double* para vector é desnecessário e leva a uma perda de desempenho.

unique_ptr pode ser usado onde você quer o desempenho de C e conveniência de C ++. Considere que você precisa operar milhões (ok, bilhões, se você ainda não confia) de strings. Armazenar cada um deles em uma string separada ou object de vector seria um desastre para as rotinas de gerenciamento de memory (heap). Especialmente se você precisar alocar e excluir strings diferentes várias vezes.

No entanto, você pode alocar um único buffer para armazenar muitas seqüências de caracteres. Você não gostaria de char* buffer = (char*)malloc(total_size); por razões óbvias (se não for óbvio, procure por “por que usar smart ptrs”). Você prefere o unique_ptr buffer(new char[total_size]);

Por analogia, as mesmas considerações de desempenho e conveniência se aplicam a dados não- char (considere milhões de vetores / matrizes / objects).

Um motivo adicional para permitir e usar std::unique_ptr , que não foi mencionado nas respostas até agora: ele permite que você declare o tipo de elemento da matriz.

Isso é útil quando você deseja minimizar as instruções #include encadeadas nos headers (para otimizar o desempenho da compilation).

Por exemplo –

myclass.h:

 class ALargeAndComplicatedClassWithLotsOfDependencies; class MyClass { ... private: std::unique_ptr m_InternalArray; }; 

myclass.cpp:

 #include "myclass.h" #include "ALargeAndComplicatedClassWithLotsOfDependencies.h" // MyClass implementation goes here 

Com a estrutura de código acima, qualquer pessoa pode #include "myclass.h" e usar MyClass , sem precisar include as dependencies de implementação internas exigidas pelo MyClass::m_InternalArray .

Se m_InternalArray fosse declarado como um std::array , ou um std::vector< ...> , respectivamente – o resultado seria uma tentativa de uso de um tipo incompleto, que é um erro de tempo de compilation.

Se você precisar de uma matriz dinâmica de objects que não sejam construtíveis para cópia, um ponteiro inteligente para uma matriz será o caminho a seguir. Por exemplo, e se você precisar de uma matriz de átomos.