É realmente necessário implementar qualquer tipo de estrutura de dados que aloque mais memória do que o mínimo necessário para o número de elementos inseridos (ou seja, qualquer coisa que não seja uma estrutura vinculada que aloque um nó por vez).
Tomar como recipientes unordered_map
, vector
ou deque
. Todos eles alocam mais memória do que é minimamente necessário para os elementos que você inseriu até o momento, para evitar a necessidade de uma alocação de heap para cada inserção única. Vamos usar vector
como o exemplo mais simples.
Quando você faz:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... que na verdade não constrói mil Foos. Simplesmente aloca / reserva memória para eles. Se vector
não usasse o posicionamento novo aqui, seria uma construção padrão em Foos
todo o lugar, além de precisar invocar seus destruidores, mesmo para elementos que você nunca inseriu em primeiro lugar.
Alocação! = Construção, Libertação! = Destruição
De um modo geral, para implementar muitas estruturas de dados como a acima, você não pode tratar a alocação de memória e a construção de elementos como uma coisa indivisível e da mesma forma não pode tratar a liberação de memória e a destruição de elementos como uma coisa indivisível.
É preciso haver uma separação entre essas idéias para evitar invocar supérfluos construtores e destruidores desnecessariamente, esquerda e direita, e é por isso que a biblioteca padrão separa a ideia std::allocator
(que não constrói ou destrói elementos quando aloca / libera memória *) longe de os contêineres que o utilizam, que constroem elementos manualmente usando posicionamento novo e destroem elementos manualmente usando invocações explícitas de destruidores.
- Eu odeio o design,
std::allocator
mas esse é um assunto diferente sobre o qual evitarei falar. :-D
De qualquer maneira, costumo usá-lo bastante desde que escrevi vários contêineres C ++ compatíveis com o padrão de uso geral que não puderam ser construídos em termos dos existentes. Entre elas, está uma pequena implementação vetorial que eu construí algumas décadas atrás, para evitar alocações de heap em casos comuns, e uma tentativa com eficiência de memória (não aloca um nó por vez). Nos dois casos, não consegui realmente implementá-los usando os contêineres existentes e, portanto, tive que usar placement new
para evitar invocar supérfluos construtores e destruidores em coisas desnecessárias, esquerda e direita.
Naturalmente, se você trabalha com alocadores personalizados para alocar objetos individualmente, como uma lista gratuita, geralmente também deseja usá-lo placement new
, como este (exemplo básico que não se preocupa com a exceção de segurança ou RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);