Estive lendo sobre contêineres STL em meu livro sobre C ++, especificamente a seção sobre o STL e seus contêineres. Agora eu entendo que cada um deles tem suas próprias propriedades específicas e estou quase memorizando todas elas ... Mas o que ainda não entendo é em que cenário cada uma delas é usada.
Qual a explicação? Código de exemplo é muito preferido.
c++
stl
container-data-type
Daniel Sloof
fonte
fonte
Respostas:
Esta folha de dicas fornece um resumo muito bom dos diferentes contêineres.
Veja o fluxograma na parte inferior como um guia sobre o qual usar em diferentes cenários de uso:
Criado por David Moore e licenciado CC BY-SA 3.0
fonte
vector
pouco vazio. stackoverflow.com/questions/10699265/…unordered_map
eunordered_set
(e suas múltiplas variantes) que não estão no fluxograma, mas são boas escolhas quando você não se importa com a ordem, mas precisa encontrar elementos por chave. Sua pesquisa geralmente é O (1) em vez de O (log n).Aqui está um fluxograma inspirado na versão de David Moore (veja acima) que eu criei, que está atualizada (principalmente) com o novo padrão (C ++ 11). Esta é apenas minha opinião pessoal, não é indiscutível, mas achei que poderia ser valioso para esta discussão:
fonte
vector (sorted)
é um pouco inconsistente com o resto. Não é um tipo diferente de contêiner, apenas o mesmo,std::vector
mas classificado. Ainda mais importante, não vejo por que não se poderia usar umastd::set
iteração ordenada, se esse é o comportamento padrão da iteração através de um conjunto. Claro, se a resposta está falando sobre o acesso ordenado aos valores do contêiner[]
, então tudo bem, você só pode fazer isso com um sotedstd::vector
. Mas em ambos os casos, a decisão deve ser tomada logo após a pergunta "ordem é necessária"Resposta simples: use
std::vector
para tudo, a menos que você tenha um motivo real para fazer o contrário.Quando você encontrar um caso em que está pensando "Nossa,
std::vector
não funciona bem aqui por causa do X", vá com base no X.fonte
std::remove_if
quase sempre é superior à abordagem "excluir durante a iteração".Veja o STL eficaz de Scott Meyers. É bom em explicar como usar o STL.
Se você deseja armazenar um número determinado / indeterminado de objetos e nunca excluir nenhum, então um vetor é o que você deseja. É a substituição padrão para uma matriz C e funciona como uma, mas não transborda. Você pode definir seu tamanho antecipadamente e também com reserve ().
Se você deseja armazenar um número indeterminado de objetos, mas irá adicioná-los e excluí-los, provavelmente deseja uma lista ... porque é possível excluir um elemento sem mover nenhum dos seguintes elementos - diferente do vetor. Porém, é preciso mais memória que um vetor e você não pode acessar sequencialmente um elemento.
Se você quiser pegar vários elementos e encontrar apenas os valores exclusivos desses elementos, a leitura de todos eles em um conjunto fará isso e os classificará para você também.
Se você possui muitos pares de valores-chave e deseja classificá-los por chave, um mapa é útil ... mas ele contém apenas um valor por chave. Se você precisar de mais de um valor por chave, poderá ter um vetor / lista como seu valor no mapa ou usar um multimap.
Não está no STL, mas está na atualização TR1 para o STL: se você tiver muitos pares de valores-chave, procurará por chave e não se importará com a ordem deles. deseja usar um hash - que é tr1 :: unordered_map. Eu usei com o Visual C ++ 7.1, onde foi chamado stdext :: hash_map. Ele tem uma pesquisa de O (1) em vez de uma pesquisa de O (log n) para o mapa.
fonte
hash_map
não é uma implementação muito boa. Espero que eles tenham se saído melhorunordered_map
.list
faz. Erro bastante gritante lá.Redesenhei o fluxograma para ter 3 propriedades:
O fluxograma:
Mais informações fornecidas neste link .
fonte
Um ponto importante apenas brevemente mencionado até agora, é que se você precisar de memória contígua (como uma matriz C dá), então você só pode usar
vector
,array
oustring
.Use
array
se o tamanho for conhecido no momento da compilação.Use
string
se você precisar trabalhar apenas com tipos de caracteres e precisar de uma sequência, não apenas de um contêiner de uso geral.Use
vector
em todos os outros casos (vector
na maioria dos casos, deve ser a escolha padrão do contêiner).Com os três itens, você pode usar a
data()
função de membro para obter um ponteiro para o primeiro elemento do contêiner.fonte
Tudo depende do que você deseja armazenar e do que deseja fazer com o contêiner. Aqui estão alguns exemplos (não exaustivos) para as classes de contêiner que eu mais uso:
vector
: Layout compacto com pouca ou nenhuma sobrecarga de memória por objeto contido. Eficiente para iterar. Anexar, inserir e apagar pode ser caro, principalmente para objetos complexos. Barato para encontrar um objeto contido por índice, por exemplo, myVector [10]. Use onde você teria usado uma matriz em C. Bom, onde você tem muitos objetos simples (por exemplo, int). Não se esqueça de usarreserve()
antes de adicionar muitos objetos ao contêiner.list
: Sobrecarga de memória pequena por objeto contido. Eficiente para iterar. Anexar, inserir e apagar são baratos. Use onde você teria usado uma lista vinculada em C.set
(emultiset
): sobrecarga significativa de memória por objeto contido. Use onde for necessário descobrir rapidamente se esse contêiner contém um determinado objeto ou mesclar contêineres com eficiência.map
(emultimap
): sobrecarga significativa de memória por objeto contido. Use onde deseja armazenar pares de valores-chave e procure valores por chave rapidamente.O fluxograma na folha de dicas sugerido por zdan fornece um guia mais completo.
fonte
Uma lição que aprendi é: tente envolvê-lo em uma classe, pois alterar o tipo de contêiner em um belo dia pode render grandes surpresas.
Não custa muito adiantado e economiza tempo na depuração quando você deseja interromper sempre que alguém faz a operação x nessa estrutura.
Chegando à seleção da estrutura de dados perfeita para um trabalho:
Cada estrutura de dados fornece algumas operações, que podem variar a complexidade do tempo:
O (1), O (lg N), O (N), etc.
Você precisa essencialmente adivinhar quais são as operações mais executadas e usar uma estrutura de dados que possua essa operação como O (1).
Simples, não é (-:
fonte
auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
typedef Collection<Foo*> CollectionOfFoo;
suficiente?I expandiu de Mikael Persson fluxograma fantástico. Adicionei algumas categorias de contêineres, o contêiner de matriz e algumas notas. Se você quiser uma cópia sua, aqui está o desenho do Google. Obrigado, Mikael, por fazer as bases! Selecionador de Contêiner C ++
fonte
Eu respondi isso em outra pergunta que está marcada como dup desta. Mas acho bom consultar alguns bons artigos sobre a decisão de escolher um contêiner padrão.
Como @David Thornley respondeu, std :: vector é o caminho a percorrer se não houver outras necessidades especiais. Este é o conselho dado pelo criador do C ++, Bjarne Stroustrup em um blog de 2014.
Aqui está o link para o artigo https://isocpp.org/blog/2014/06/stroustrup-lists
e citar esse,
Nos comentários, o usuário @NathanOliver também fornece outro bom blog, que possui medidas mais concretas. https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html .
fonte