Qual é o capacity()
de um std::vector
que é criado usando o construtor padrão? Eu sei que o size()
é zero. Podemos afirmar que um vetor construído padrão não chama a alocação de memória heap?
Dessa forma seria possível criar um array com reserva arbitrária usando uma única alocação, como std::vector<int> iv; iv.reserve(2345);
. Digamos que, por algum motivo, eu não queira iniciar size()
no 2345.
Por exemplo, no Linux (g ++ 4.4.5, kernel 2.6.32 amd64)
#include <iostream>
#include <vector>
int main()
{
using namespace std;
cout << vector<int>().capacity() << "," << vector<int>(10).capacity() << endl;
return 0;
}
impresso 0,10
. É uma regra ou depende do fornecedor de STL?
c++
memory-management
stl
vector
Não está na lista
fonte
fonte
swap
todos os iteradores e referências permanecem válidos (excetoend()
s). Isso significa que um buffer embutido não é possível.Respostas:
O padrão não especifica qual
capacity
deve ser a inicial de um contêiner, então você está contando com a implementação. Uma implementação comum iniciará a capacidade em zero, mas não há garantia. Por outro lado, não há como melhorar sua estratégia destd::vector<int> iv; iv.reserve(2345);
ficar com ela.fonte
vector::reserve
não é o mesmo que especificar um tamanho inicial. Os construtores de vetor que recebem um valor de tamanho / cópia inicial inicializamn
objetos e, portanto, têm complexidade linear. OTOH, chamar reserva significa apenas copiar / moversize()
elementos se uma realocação for acionada. Em um vetor vazio, não há nada para copiar. Portanto, o último pode ser desejável mesmo se a implementação alocar memória para um vetor construído padrão.As implementações de armazenamento de std :: vector variam significativamente, mas todas as que encontrei começam em 0.
O seguinte código:
#include <iostream> #include <vector> int main() { using namespace std; vector<int> normal; cout << normal.capacity() << endl; for (unsigned int loop = 0; loop != 10; ++loop) { normal.push_back(1); cout << normal.capacity() << endl; } cin.get(); return 0; }
Dá a seguinte saída:
sob GCC 5.1 e:
sob MSVC 2013.
fonte
Até onde eu entendi o padrão (embora eu não pudesse nomear uma referência), a instanciação do contêiner e a alocação de memória foram intencionalmente desacopladas por um bom motivo. Portanto, você tem chamadas distintas e separadas para
constructor
para criar o próprio contêinerreserve()
para pré-alocar um bloco de memória adequadamente grande para acomodar pelo menos (!) um determinado número de objetosE isso faz muito sentido. O único direito de existir
reserve()
é dar a você a oportunidade de codificar em torno de realocações possivelmente caras ao aumentar o vetor. Para ser útil, você deve saber o número de objetos a armazenar ou, pelo menos, ser capaz de fazer um palpite. Se isso não for fornecido, é melhor você ficar longe,reserve()
pois você apenas mudará a realocação para memória desperdiçada.Então, juntando tudo:
reserve()
e não precisa estar no mesmo local de construção (poderia / deveria ser mais tarde, depois que você tomar conhecimento do tamanho necessário para acomodar)reserve()
, não é?push_back()
- se já não estiver explicitamente alocado antes porreserve()
.Tudo isso só funciona e tem vantagem se não for perturbado por um construtor de alocação. Você tem padrões razoáveis para cenários comuns que podem ser substituídos sob demanda por
reserve()
(eshrink_to_fit()
). Portanto, mesmo se o padrão não declarar isso explicitamente, estou certo de que assumir que um vetor recém-construído não pré-aloca é uma aposta bastante segura para todas as implementações atuais.fonte
Como uma ligeira adição às outras respostas, descobri que, ao executar em condições de depuração com o Visual Studio, um vetor padrão construído ainda será alocado no heap, embora a capacidade comece em zero.
Especificamente se _ITERATOR_DEBUG_LEVEL! = 0 então o vetor alocará algum espaço para ajudar na verificação do iterador.
https://docs.microsoft.com/en-gb/cpp/standard-library/iterator-debug-level
Achei isso um pouco irritante, já que estava usando um alocador personalizado na época e não esperava a alocação extra.
fonte
Esta é uma pergunta antiga, e todas as respostas aqui explicaram corretamente o ponto de vista do padrão e a maneira como você pode obter uma capacidade inicial de forma portátil usando
std::vector::reserve
;No entanto, explicarei por que não faz sentido para nenhuma implementação STL alocar memória durante a construção de um
std::vector<T>
objeto ;std::vector<T>
de tipos incompletos;Antes do C ++ 17, era um comportamento indefinido construir um
std::vector<T>
se a definição deT
ainda for desconhecida no ponto de instanciação. No entanto, essa restrição foi relaxada em C ++ 17 .Para alocar memória de maneira eficiente para um objeto, você precisa saber seu tamanho. Do C ++ 17 e além, seus clientes podem ter casos em que sua
std::vector<T>
classe não sabe o tamanho deT
. Faz sentido ter características de alocação de memória dependentes da integridade do tipo?Unwanted Memory allocations
Muitas, muitas vezes você precisará modelar um gráfico no software. (Uma árvore é um gráfico); Você provavelmente irá modelá-lo como:
class Node { .... std::vector<Node> children; //or std::vector< *some pointer type* > children; .... };
Agora pense por um momento e imagine se você tivesse muitos nós terminais. Você ficaria muito chateado se sua implementação STL alocasse memória extra simplesmente na expectativa de ter objetos
children
.Este é apenas um exemplo, fique à vontade para pensar em mais ...
fonte
Padrão não especifica o valor inicial para capacidade, mas o contêiner STL cresce automaticamente para acomodar tantos dados quanto você coloca, desde que você não exceda o tamanho máximo (use a função de membro max_size para saber). Para vetor e string, o crescimento é controlado por realocar sempre que mais espaço for necessário. Suponha que você gostaria de criar um vetor contendo o valor de 1-1000. Sem usar reserva, o código normalmente resultará em 2 a 18 realocações durante o seguinte loop:
vector<int> v; for ( int i = 1; i <= 1000; i++) v.push_back(i);
Modificar o código para usar reserva pode resultar em 0 alocações durante o loop:
vector<int> v; v.reserve(1000); for ( int i = 1; i <= 1000; i++) v.push_back(i);
A grosso modo, as capacidades do vetor e da string aumentam por um fator entre 1,5 e 2 a cada vez.
fonte