Alguém aqui já usou o "posicionamento novo" do C ++? Se sim, para quê? Parece-me que só seria útil em hardware mapeado na memória.
c++
memory-management
new-operator
placement-new
Head Geek
fonte
fonte
p = pt
usar e usar o operador de atribuição emPoint
vez de o fazernew(&p) Point(pt)
? Eu me pergunto as diferenças entre os dois. O primeiro chamariaoperator=
Point, enquanto o último chama o construtor de cópias dePoint
? mas ainda não estou muito claro por que um é melhor que o outro.U::operator=
acabou de ser chamado.Respostas:
O posicionamento new permite construir um objeto na memória que já está alocado.
Você pode fazer isso para otimização quando precisar criar várias instâncias de um objeto, e é mais rápido não alocar memória novamente sempre que precisar de uma nova instância. Em vez disso, pode ser mais eficiente executar uma alocação única para um pedaço de memória que pode conter vários objetos, mesmo que você não queira usá-lo de uma só vez.
O DevX dá um bom exemplo :
Você também pode ter certeza de que não pode haver falha na alocação em uma determinada parte do código crítico (por exemplo, no código executado por um marcapasso). Nesse caso, você deseja alocar memória mais cedo e, em seguida, use o posicionamento new na seção crítica.
Desalocação na veiculação novo
Você não deve desalocar todos os objetos que estão usando o buffer de memória. Em vez disso, você deve excluir [] apenas o buffer original. Você teria que chamar os destruidores de suas classes manualmente. Para uma boa sugestão, consulte as Perguntas frequentes do Stroustrup sobre: Existe uma "exclusão de canal" ?
fonte
delete[]
ochar
buffer original . O uso da veiculaçãonew
encerrou a vida útil doschar
objetos originais , reutilizando seu armazenamento. Se você chamardelete[] buf
o tipo dinâmico do (s) objeto (s) apontado (s) para não corresponder mais ao tipo estático, você terá um comportamento indefinido. É mais consistente usaroperator new
/operator delete
alocar memória bruta pretendida para uso por posicionamentonew
.#include <new>
.Nós o usamos com conjuntos de memória personalizados. Apenas um esboço:
Agora você pode agrupar objetos em uma única arena de memória, selecionar um alocador muito rápido, mas sem desalocação, usar mapeamento de memória e qualquer outra semântica que desejar impor, escolhendo o pool e passando-o como argumento para a localização de um objeto novo operador.
fonte
allocate()
algum lugar?É útil se você deseja separar a alocação da inicialização. STL usa o posicionamento new para criar elementos de contêiner.
fonte
Eu usei na programação em tempo real. Normalmente , não queremos realizar nenhuma alocação dinâmica (ou desalocação) após a inicialização do sistema, porque não há garantia de quanto tempo isso levará.
O que posso fazer é pré-alocar um grande pedaço de memória (grande o suficiente para armazenar qualquer quantidade que a classe exigir). Então, quando eu descobrir em tempo de execução como construir as coisas, o posicionamento new pode ser usado para construir objetos exatamente onde eu os quero. Uma situação em que sei que o usei foi ajudar a criar um buffer circular heterogêneo .
Certamente não é para os fracos de coração, mas é por isso que eles criam a sintaxe para isso de maneira meio esquisita.
fonte
Eu usei para construir objetos alocados na pilha via alloca ().
plugue sem vergonha: eu escrevi sobre isso aqui .
fonte
boost::array
. Você pode expandir um pouco isso?Geek principal: BINGO! Você entendeu totalmente - é exatamente para isso que é perfeito. Em muitos ambientes incorporados, restrições externas e / ou o cenário de uso geral força o programador a separar a alocação de um objeto da sua inicialização. Agrupados, o C ++ chama isso de "instanciação"; mas sempre que a ação do construtor deve ser explicitamente invocada SEM alocação dinâmica ou automática, o posicionamento new é a maneira de fazê-lo. É também a maneira perfeita de localizar um objeto C ++ global fixado no endereço de um componente de hardware (E / S mapeada na memória) ou em qualquer objeto estático que, por qualquer motivo, precise residir em um endereço fixo.
fonte
Eu usei para criar uma classe Variant (ou seja, um objeto que pode representar um valor único que pode ser um de vários tipos diferentes).
Se todos os tipos de valor suportados pela classe Variant são do tipo POD (por exemplo, int, float, double, bool), uma união no estilo C marcada é suficiente, mas se você deseja que alguns tipos de valor sejam objetos C ++ ( por exemplo, std :: string), o recurso de união C não funciona, pois tipos de dados que não são POD podem não ser declarados como parte de uma união.
Então, em vez disso, aloco uma matriz de bytes que seja grande o suficiente (por exemplo, sizeof (the_largest_data_type_I_support)) e uso a colocação new para inicializar o objeto C ++ apropriado nessa área quando a Variant estiver configurada para conter um valor desse tipo. (E o posicionamento é excluído antecipadamente ao sair de um tipo de dados diferente de POD, é claro)
fonte
new
para inicializar sua subclasse não-POD. Ref: stackoverflow.com/a/33289972/2757035 Reinventar esta roda usando uma matriz de bytes arbitrariamente grande é uma peça impressionante de acrobacias, mas parece totalmente desnecessária. Então, o que eu perdi? :)O posicionamento new também é muito útil ao serializar (por exemplo, com boost :: serialization). Em 10 anos de c ++, este é apenas o segundo caso para o qual eu precisava de um novo posicionamento (terceiro, se você incluir entrevistas :)).
fonte
Também é útil quando você deseja reinicializar estruturas globais ou alocadas estaticamente.
O antigo modo C estava usando
memset()
para definir todos os elementos como 0. Você não pode fazer isso no C ++ devido a vtables e construtores de objetos personalizados.Às vezes eu uso o seguinte
fonte
Acho que isso não foi destacado por nenhuma resposta, mas outro bom exemplo e uso para o novo canal é reduzir a fragmentação da memória (usando conjuntos de memória). Isso é especialmente útil em sistemas embarcados e de alta disponibilidade. Neste último caso, é especialmente importante porque, para um sistema que precisa ser executado 24/365 dias, é muito importante não haver fragmentação. Esse problema não tem nada a ver com vazamento de memória.
Mesmo quando uma implementação malloc muito boa é usada (ou função de gerenciamento de memória semelhante), é muito difícil lidar com a fragmentação por um longo tempo. Em algum momento, se você não gerenciar de maneira inteligente as chamadas de reserva / liberação de memória, poderá acabar com muitas pequenas lacunas difíceis de reutilizar (atribuir a novas reservas). Portanto, uma das soluções usadas nesse caso é usar um pool de memória para alocar antes da mão a memória para os objetos de aplicativo. Depois de cada vez que você precisar de memória para algum objeto, basta usar o novo posicionamento para criar um novo objeto na memória já reservada.
Dessa forma, uma vez iniciado o aplicativo, você já terá toda a memória necessária reservada. Toda a nova reserva / liberação de memória vai para os conjuntos alocados (você pode ter vários conjuntos, um para cada classe de objeto diferente). Nenhuma fragmentação de memória ocorre neste caso, pois não haverá falhas e seu sistema poderá funcionar por períodos muito longos (anos) sem sofrer fragmentação.
Vi isso na prática especialmente para o VxWorks RTOS, pois seu sistema de alocação de memória padrão sofre muito com a fragmentação. Portanto, alocar memória pelo método new / malloc padrão era basicamente proibido no projeto. Todas as reservas de memória devem ir para um conjunto de memórias dedicado.
fonte
É 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
oudeque
. 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 usarvector
como o exemplo mais simples.Quando você faz:
... 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 emFoos
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.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):fonte
É útil se você estiver construindo um kernel - onde você coloca o código do kernel que lê do disco ou da paginação? Você precisa saber para onde pular.
Ou, em outras circunstâncias, muito raras, como quando você tem muito espaço alocado e deseja colocar algumas estruturas atrás uma da outra. Eles podem ser empacotados dessa maneira sem a necessidade do operador offsetof (). Existem outros truques para isso também.
Eu também acredito que algumas implementações STL fazem uso de novas colocações, como std :: vector. Eles alocam espaço para 2 ^ n elementos dessa maneira e nem sempre precisam realocar.
fonte
É usado
std::vector<>
porquestd::vector<>
normalmente aloca mais memória do que háobjects
novector<>
.fonte
Eu usei para armazenar objetos com arquivos mapeados na memória.
O exemplo específico foi um banco de dados de imagens que processou um grande número de imagens grandes (mais do que poderia caber na memória).
fonte
Eu já vi isso como um pequeno hack de desempenho para um ponteiro de "tipo dinâmico" (na seção "Sob o capô"):
fonte
void*
leva 8 bytes. É um pouco tolo apontar oito bytesvoid*
para um bytebool
. Mas é perfeitamente possível sobreporbool
ovoid*
, como umunion { bool b; void* v }
. Você precisa de alguma maneira de saber que a coisa que você chamou devoid*
na verdade é abool
(ou ashort
, ou afloat
etc.). O artigo ao qual vinculei descreve como fazer isso. E, para responder à pergunta original, a veiculaçãonew
é o recurso usado para criar umbool
(ou outro tipo) onde avoid*
é esperado (as conversões são usadas para obter / modificar posteriormente o valor).Usei-o para criar objetos com base na memória contendo mensagens recebidas da rede.
fonte
Geralmente, o posicionamento new é usado para se livrar do custo de alocação de um 'novo normal'.
Outro cenário em que o usei é um local em que eu queria ter acesso ao ponteiro para um objeto que ainda deveria ser construído, para implementar um singleton por documento.
fonte
Pode ser útil ao usar memória compartilhada, entre outros usos ... Por exemplo: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions. conditions_anonymous_example
fonte
O único lugar que eu encontrei é em contêineres que alocam um buffer contíguo e o preenchem com os objetos, conforme necessário. Como mencionado, o std :: vector pode fazer isso, e eu sei que algumas versões do MFC CArray e / ou CList fizeram isso (porque é onde eu o encontrei pela primeira vez). O método de superalocação de buffer é uma otimização muito útil, e a colocação de novo é praticamente a única maneira de construir objetos nesse cenário. Às vezes, também é usado para construir objetos em blocos de memória alocados fora do seu código direto.
Eu o usei em uma capacidade semelhante, embora não apareça com frequência. É uma ferramenta útil para a caixa de ferramentas C ++.
fonte
Os mecanismos de script podem usá-lo na interface nativa para alocar objetos nativos dos scripts. Veja Angelscript (www.angelcode.com/angelscript) para exemplos.
fonte
Consulte o arquivo fp.h no projeto xll em http://xll.codeplex.com. Ele resolve o problema de "chulminess injustificado com o compilador" para matrizes que gostam de carregar suas dimensões com elas.
fonte
Aqui está o uso matador do construtor C ++ in-loco: alinhando-se a uma linha de cache, além de outras potências de 2 limites. Aqui está o meu algoritmo de alinhamento de ponteiro ultra-rápido para qualquer potência de 2 limites com 5 ou menos instruções de ciclo único :
Agora isso não apenas coloca um sorriso em seu rosto (:-). Eu ♥♥♥ C ++ 1x
fonte