std::unique_ptr
tem suporte para matrizes, por exemplo:
std::unique_ptr<int[]> p(new int[10]);
mas é necessário? provavelmente é mais conveniente usar std::vector
ou std::array
.
Você encontra algum uso para essa construção?
std::unique_ptr
tem suporte para matrizes, por exemplo:
std::unique_ptr<int[]> p(new int[10]);
mas é necessário? provavelmente é mais conveniente usar std::vector
ou std::array
.
Você encontra algum uso para essa construção?
std::shared_ptr<T[]>
, mas deveria existir e provavelmente estará no C ++ 14 se alguém se desse ao trabalho de escrever uma proposta. Enquanto isso, sempre háboost::shared_array
.std::shared_ptr
<T []> está em c ++ 17 agora.Respostas:
Algumas pessoas não têm o luxo de usar
std::vector
, mesmo com alocadores. Algumas pessoas precisam de uma matriz de tamanho dinâmico, entãostd::array
está fora. E algumas pessoas obtêm suas matrizes de outro código conhecido por retornar uma matriz; e esse código não será reescrito para retornar umvector
ou algo assim.Ao permitir
unique_ptr<T[]>
, você atende a essas necessidades.Em suma, você usa
unique_ptr<T[]>
quando precisa . Quando as alternativas simplesmente não funcionarão para você. É uma ferramenta de último recurso.fonte
vector
". Você pode argumentar se esses requisitos são razoáveis ou não, mas não pode negar que eles existem .std::vector
se puderstd::unique_ptr
.unique_ptr
, mas esses tipos de projetos realmente existem.Existem compensações e você escolhe a solução que corresponde ao que deseja. Em cima da minha cabeça:
Tamanho inicial
vector
eunique_ptr<T[]>
permitir que o tamanho seja especificado no tempo de execuçãoarray
permite apenas que o tamanho seja especificado em tempo de compilaçãoRedimensionando
array
eunique_ptr<T[]>
não permite redimensionarvector
fazArmazenamento
vector
eunique_ptr<T[]>
armazene os dados fora do objeto (normalmente na pilha)array
armazena os dados diretamente no objetoCopiando
array
evector
permitir copiarunique_ptr<T[]>
não permite copiarTrocar / mover
vector
eunique_ptr<T[]>
tem tempo O (1)swap
operações de e movimentaçãoarray
possui O (n)swap
operações de tempo e movimentação, em que n é o número de elementos na matrizAnulação de ponteiro / referência / iterador
array
garante que ponteiros, referências e iteradores nunca serão invalidados enquanto o objeto estiver ativo, mesmo emswap()
unique_ptr<T[]>
não possui iteradores; ponteiros e referências são invalidados apenasswap()
enquanto o objeto estiver ativo. (Após a troca, os ponteiros apontam para a matriz com a qual você trocou, portanto eles ainda são "válidos" nesse sentido.)vector
pode invalidar ponteiros, referências e iteradores em qualquer realocação (e fornece algumas garantias de que a realocação só pode ocorrer em determinadas operações).Compatibilidade com conceitos e algoritmos
array
evector
são ambos contêineresunique_ptr<T[]>
não é um contêinerEu tenho que admitir, isso parece uma oportunidade para alguma refatoração com design baseado em políticas.
fonte
vector
. Depois, você aumenta o tamanho ou a capacidade devector
tal forma que força uma realocação. Em seguida, esse iterador, ponteiro ou referência não aponta mais para esse elemento dovector
. É isso que queremos dizer com "invalidação". Esse problema não ocorrearray
, porque não há "realocação". Na verdade, eu só notei um detalhe com isso e o editei para se adequar.unique_ptr<T[]>
porque não há realocação. Mas é claro que, quando a matriz ficar fora do escopo, os ponteiros para elementos específicos ainda serão invalidados.T[]
, o tamanho (ou informações equivalentes) deve estar em algum lugar paraoperator delete[]
destruir corretamente os elementos da matriz. Seria bom se o programador tivesse acesso a isso.Um motivo para usar a
unique_ptr
é se você não quiser pagar o custo de tempo de execução da inicialização de valor da matriz.O
std::vector
construtor estd::vector::resize()
inicializará o valorT
- masnew
não fará isso seT
for um POD.Consulte Objetos com valor inicializado em C ++ 11 e construtor std :: vector
Observe que
vector::reserve
não é uma alternativa aqui: O acesso ao ponteiro bruto após std :: vector :: reserve é seguro?É a mesma razão que um programador C pode escolher
malloc
maiscalloc
.fonte
std::vector
um alocador personalizado que evite a construção de tipos que sãostd::is_trivially_default_constructible
e a destruição de objetos que sãostd::is_trivially_destructible
, embora isso estritamente viole o padrão C ++ (já que esses tipos não são inicializados por padrão).std::unique_ptr
não fornece nenhuma verificação vinculada, contrária a muitasstd::vector
implementações.std::vector
é exigido pela Norma para verificar os limites.at()
. Eu acho que você quis dizer que algumas implementações também têm modos de depuração.operator[]
, mas considero isso inútil para escrever código bom e portátil.Um
std::vector
pode ser copiado, enquantounique_ptr<int[]>
permite expressar a propriedade exclusiva da matriz.std::array
, por outro lado, exige que o tamanho seja determinado no tempo de compilação, o que pode ser impossível em algumas situações.fonte
unique_ptr
vez deshared_ptr
. Estou esquecendo de algo?unique_ptr
faz mais do que apenas impedir o uso indevido acidental. Também é menor e menor sobrecarga do queshared_ptr
. O ponto é que, embora seja bom ter semântica em uma classe que impeça o "uso indevido", esse não é o único motivo para usar um tipo específico. Evector
é muito mais útil como armazenamento de matriz do queunique_ptr<T[]>
, se não por outro motivo, a não ser pelo fato de ter um tamanho .vector
maisunique_ptr<T[]>
, sempre que possível, em vez de apenas dizer, "você não pode copiá-lo" e, portanto, escolherunique_ptr<T[]>
quando você não quer cópias. Impedir que alguém faça a coisa errada não é necessariamente o motivo mais importante para escolher uma aula.std::vector
tem mais sobrecarga que astd::unique_ptr
- usa ~ 3 ponteiros em vez de ~ 1.std::unique_ptr
bloqueia a construção da cópia, mas permite a construção da movimentação, que se semanticamente os dados com os quais você está trabalhando só podem ser movidos, mas não copiados, infectam aclass
contendo os dados. Ter uma operação com dados que não são válidos realmente piora a sua classe de contêineres, e "simplesmente não o use" não lava todos os pecados. Ter que colocar todas as suas instânciasstd::vector
em uma classe em que você desabilita manualmentemove
é uma dor de cabeça.std::unique_ptr<std::array>
tem umsize
.Scott Meyers tem isso a dizer em Effective Modern C ++
Penso que a resposta de Charles Salvia é relevante: essa
std::unique_ptr<T[]>
é a única maneira de inicializar uma matriz vazia cujo tamanho não é conhecido em tempo de compilação. O que Scott Meyers tem a dizer sobre essa motivação para usarstd::unique_ptr<T[]>
?fonte
vector
stackoverflow.com/a/24852984/2436175 .Ao contrário de
std::vector
estd::array
,std::unique_ptr
pode possuir um ponteiro NULL.Isso é útil ao trabalhar com APIs C que esperam uma matriz ou NULL:
fonte
Eu usei
unique_ptr<char[]>
para implementar um pool de memória pré-alocado usado em um mecanismo de jogo. A idéia é fornecer pools de memória 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 ter que alocar / liberar memória em cada quadro. É bastante conveniente para esse tipo de cenário em que você precisa de conjuntos de memória para alocar objetos com tempo de vida útil limitado (geralmente um, 2 ou 3 quadros) que não exigem lógica de destruição (apenas desalocação de memória).fonte
Um padrão comum pode ser encontrado em algumas chamadas da API do Windows Win32 , nas quais o uso
std::unique_ptr<T[]>
pode ser útil, por exemplo, quando você não sabe exatamente qual deve ser o tamanho de um buffer de saída ao chamar alguma API do Win32 (que grava alguns dados dentro esse buffer):fonte
std::vector<char>
nesses casos.Eu enfrentei um caso em que eu tinha que usar
std::unique_ptr<bool[]>
, que estava na biblioteca HDF5 (uma biblioteca para armazenamento eficiente de dados binários, muito usada na ciência). Alguns compiladores (Visual Studio 2015 no meu caso) fornecem compactaçãostd::vector<bool>
(usando 8 bools em cada byte), que é uma catástrofe para algo como o HDF5, que não se importa com essa compactação. Comstd::vector<bool>
, o HDF5 acabou lendo o lixo por causa dessa compressão.Adivinha quem estava lá para o resgate, em um caso em
std::vector
que não funcionava, e eu precisava alocar uma matriz dinâmica de maneira limpa? :-)fonte
Em poucas palavras: é de longe o mais eficiente em termos de memória.
A
std::string
vem com um ponteiro, um comprimento e um buffer de "otimização de cadeia curta". Mas minha situação é que preciso armazenar uma string quase sempre vazia, em uma estrutura da qual tenho centenas de milhares. Em C, eu usaria apenaschar *
, e seria nulo na maioria das vezes. O que também funciona para C ++, exceto que achar *
não possui destruidor e não sabe se excluir. Por outro lado, astd::unique_ptr<char[]>
se exclui quando sai do escopo. Um vaziostd::string
ocupa 32 bytes, mas um vaziostd::unique_ptr<char[]>
ocupa 8 bytes, ou seja, exatamente o tamanho do seu ponteiro.A maior desvantagem é que toda vez que eu quero saber o comprimento da corda, tenho que ligar
strlen
para ela.fonte
Para responder às pessoas que pensam que você "precisa" usar, em
vector
vez deunique_ptr
eu ter um caso na programação CUDA na GPU ao alocar memória no dispositivo, você deve procurar uma matriz de ponteiros (comcudaMalloc
). Em seguida, ao recuperar esses dados no Host, você deve procurar novamente um ponteiro e nãounique_ptr
há problema em manipular o ponteiro facilmente. O custo extra da conversãodouble*
paravector<double>
é desnecessário e leva a uma perda de desempenho.fonte
Um motivo adicional para permitir e usar
std::unique_ptr<T[]>
, que não foi mencionado nas respostas até agora: permite que você declare o tipo de elemento da matriz.Isso é útil quando você deseja minimizar o encadeamento
#include
instruções nos cabeçalhos (para otimizar o desempenho da compilação).Por exemplo -
Com a estrutura de código acima, qualquer pessoa pode
#include "myclass.h"
e pode usarMyClass
, sem precisar incluir as dependências de implementação internas exigidas porMyClass::m_InternalArray
.Se
m_InternalArray
fosse declarado como astd::array<ALargeAndComplicatedClassWithLotsOfDependencies>
, ou astd::vector<...>
, respectivamente - o resultado seria a tentativa de uso de um tipo incompleto, que é um erro em tempo de compilação.fonte
class ALargeAndComplicatedClassWithLotsOfDependencies
. Então, logicamente, você não deve se deparar com esses cenários.Não posso discordar com o espírito da resposta aceita com força suficiente. "Uma ferramenta de último recurso"? Longe disso!
A meu ver, um dos recursos mais fortes do C ++ em comparação com o C e com outras linguagens semelhantes é a capacidade de expressar restrições para que possam ser verificadas em tempo de compilação e que o uso indevido acidental possa ser evitado. Portanto, ao projetar uma estrutura, pergunte-se que operações ela deve permitir. Todos os outros usos devem ser proibidos, e é melhor que essas restrições possam ser implementadas estaticamente (em tempo de compilação), para que o uso indevido resulte em falha na compilação.
Portanto, quando alguém precisa de uma matriz, as respostas para as seguintes perguntas especificam seu comportamento: 1. Seu tamanho é a) dinâmico em tempo de execução ou b) estático, mas conhecido apenas em tempo de execução ou c) estático e conhecido em tempo de compilação? 2. A matriz pode ser alocada na pilha ou não?
E, com base nas respostas, é isso que considero a melhor estrutura de dados para uma matriz desse tipo:
Sim eu acho
unique_ptr<std::array>
que também deve ser considerado, e nem é uma ferramenta de último recurso. Basta pensar no que melhor se ajusta ao seu algoritmo.Tudo isso é compatível com APIs C simples por meio do ponteiro bruto para a matriz de dados (
vector.data()
/array.data()
/uniquePtr.get()
).PS Além das considerações acima, há também uma propriedade:
std::array
estd::vector
possui semântica de valor (possui suporte nativo para copiar e transmitir por valor), enquantounique_ptr<T[]>
só pode ser movida (aplica a propriedade única). Qualquer um pode ser útil em diferentes cenários. Pelo contrário, matrizes estáticas simples (int[N]
) e dinâmicas simples (new int[10]
) não oferecem e, portanto, devem ser evitadas se possível - o que deve ser possível na grande maioria dos casos. Se isso não bastasse, matrizes dinâmicas simples também não oferecem maneira de consultar seu tamanho - oportunidade extra para corrupção de memória e falhas de segurança.fonte
Eles podem ser a resposta mais correta possível quando você só consegue apontar um único ponteiro por meio de uma API existente (mensagem da janela de reflexão ou parâmetros de retorno de chamada relacionados ao encadeamento) que têm alguma medida da vida útil após serem "capturados" no outro lado da hachura, mas que não está relacionado ao código de chamada:
Todos nós queremos que as coisas sejam agradáveis para nós. C ++ é para os outros momentos.
fonte
unique_ptr<char[]>
pode ser usado onde você deseja o desempenho de C e a conveniência de C ++. Considere que você precisa operar com milhões (ok, bilhões, se você ainda não confia) de strings. Armazenar cada um deles em um objetostring
ou separadovector<char>
seria um desastre para as rotinas de gerenciamento de memória (heap). Especialmente se você precisar alocar e excluir diferentes strings várias vezes.No entanto, você pode alocar um único buffer para armazenar tantas seqüências de caracteres. Você não gostaria
char* buffer = (char*)malloc(total_size);
por razões óbvias (se não óbvio, procure "por que usar smart ptrs"). Você prefereunique_ptr<char[]> buffer(new char[total_size]);
Por analogia, as mesmas considerações de desempenho e conveniência se aplicam a não
char
dados (considere milhões de vetores / matrizes / objetos).fonte
vector<char>
? A resposta, suponho, é porque eles serão inicializados com zero quando você criar o buffer, enquanto não serão se você usarunique_ptr<char[]>
. Mas esta pepita de chave está faltando na sua resposta.new[]
std::vector
, por exemplo, para impedir que programadores descuidados introduzam cópias acidentalmenteExiste uma regra geral de que os contêineres C ++ devem ser preferidos ao invés de rolar o seu próprio com ponteiros. É uma regra geral; tem exceções. Tem mais; estes são apenas exemplos.
fonte
Se você precisar de uma matriz dinâmica de objetos que não sejam construtíveis para cópia, o caminho a seguir é um ponteiro inteligente para uma matriz. Por exemplo, e se você precisar de uma matriz atômica.
fonte