O novo sempre aloca no heap em C ++ / C # / Java

Meu entendimento sempre foi, independentemente de C ++, C # ou Java, que quando usamos a new palavra-chave para criar um object, ele aloca memory no heap. Eu pensei que o new é necessário somente para tipos de referência (classs), e que tipos primitivos (int, bool, float, etc.) nunca usam new e sempre vão na pilha (exceto quando eles são uma variável de membro de uma class que é instanciado com new ). No entanto, tenho lido informações que me fazem duvidar dessa suposição de longa data, pelo menos para Java e C #.

Por exemplo, só notei que em C # o new operador pode ser usado para inicializar um tipo de valor, veja aqui . Isso é uma exceção à regra, um recurso auxiliar da linguagem e, em caso afirmativo, que outras exceções haveria?

Alguém pode esclarecer isso?

Eu pensei que o novo só é necessário para tipos de referência (classs), e que os tipos primitivos (int, bool, float, etc.) nunca usam novos

Em C ++, você pode alocar tipos primitivos no heap se quiser:

 int* p = new int(42); 

Isso é útil se você quiser um contador compartilhado, por exemplo, na implementação de shared_ptr .

Além disso, você não é obrigado a usar o novo com classs em C ++:

 void function() { MyClass myObject(1, 2, 3); } 

Isso alocará myObject na pilha. Observe que o new é raramente usado no C ++ moderno.

Além disso, você pode sobrecarregar o operator new (globalmente ou específico da class) em C ++, portanto, mesmo se você disser new MyClass , o object não necessariamente será alocado no heap.

Eu não sei exatamente sobre Java (e parece bastante difícil obter uma documentação sobre isso).

Em C #, new invoca o construtor e retorna um novo object. Se for do tipo valor, ele é alocado na pilha (por exemplo, variável local) ou no heap (por exemplo, object em checkbox, membro de um object de tipo de referência). Se for do tipo de referência, sempre vai no heap e é gerenciado pelo coletor de lixo. Veja http://msdn.microsoft.com/en-us/library/fa0ab757(v=vs.80).aspx para mais detalhes.

Em C ++, uma “nova expressão” retorna um ponteiro para um object com duração de armazenamento dynamic (ou seja, você deve se destruir). Não há menção de heap (com este significado) no padrão C ++, e o mecanismo pelo qual tal object é obtido é definido pela implementação.

Meu entendimento sempre foi, independentemente de C ++, C # ou Java, que quando usamos a new palavra-chave para criar um object, ele aloca memory no heap.

Sua compreensão está incorreta:

  • new pode funcionar de maneira diferente em diferentes linguagens de programação, mesmo quando essas linguagens são superficialmente semelhantes. Não deixe que a syntax semelhante de C #, C ++ e Java engane você!

  • Os termos “heap” e “pilha” (como são entendidos no contexto do gerenciamento de memory interna) simplesmente não são relevantes para todas as linguagens de programação. Indiscutivelmente, esses dois conceitos são mais frequentemente detalhes de implementação do que eles fazem parte da especificação oficial de uma linguagem de programação.

    (IIRC, isso é verdade para pelo menos C # e C ++. Eu não sei sobre Java.)

    O fato de que eles são tão difundidos detalhes de implementação não implica que você deve confiar nessa distinção, nem que você deve mesmo saber sobre isso! (No entanto, admito que geralmente acho benéfico saber “como as coisas funcionam” internamente.)

Eu sugiro que você pare de se preocupar muito com esses conceitos. O importante que você precisa acertar é entender a semântica de uma linguagem; por exemplo, para C # ou qualquer outra linguagem .NET, a diferença na semântica do tipo de referência e valor.

Exemplo: O que a especificação C # diz sobre o operador new :

Observe como a seguinte parte da especificação C # publicada pela ECMA (4ª edição) não menciona nenhuma “pilha” ou “heap”:

14.5.10 O novo operador

O novo operador é usado para criar novas instâncias de tipos. […]

O novo operador implica a criação de uma instância de um tipo, mas não necessariamente implica em alocação dinâmica de memory. Em particular, instâncias de tipos de valor não requerem memory adicional além das variables ​​em que residem e nenhuma alocação dinâmica ocorre quando new é usado para criar instâncias de tipos de valor.

Em vez disso, ele fala de “alocação dinâmica de memory”, mas isso não é a mesma coisa: você poderia alocar dinamicamente a memory em uma pilha, no heap ou em qualquer outro lugar (por exemplo, em uma unidade de disco rígido).

O que ele diz, no entanto, é que instâncias de tipos de valor são armazenadas no local, o que é exatamente o que a semântica do tipo de valor significa: instâncias de tipo de valor são copiadas durante uma atribuição, enquanto que instâncias de tipo de referência são referenciadas / “aliased”. Essa é a coisa importante para entender, não o “heap” ou a “pilha”!

Em c #, uma class sempre vive no heap. Uma struct pode estar no heap ou na pilha:

  • variables ​​(exceto capturas e blocos iteradores) e campos em uma estrutura que está na pilha ao vivo na pilha
  • capturas, blocos do iterador, campos de algo que está no heap e valores em uma matriz ao vivo no heap, assim como os valores “em checkbox”

O Java 7 evita a análise para determinar se um object pode ser alocado na pilha, de acordo com http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html .

No entanto, você não pode instruir o tempo de execução para alocar um object no heap ou na pilha. Isso é feito automaticamente.

Em relação ao c #, leia A verdade sobre os tipos de valor . Você verá que os tipos de valor também podem ser incluídos no heap.

E nesta questão é sugerido que os tipos de referência poderiam ir na pilha. (mas isso não acontece no momento)

(Referindo-se a Java) O que você disse está correto – os primitivos são alocados na pilha (há exceções, por exemplo, closures). No entanto, você pode estar se referindo a objects como:

 Integer n = new Integer(2); 

Isso se refere a um object Integer e não a um primitivo int. Talvez esta fosse sua fonte de confusão? Nesse caso, n será alocado no heap. Talvez sua confusão tenha sido causada pelas regras de autoboxing ? Veja também esta questão para mais detalhes sobre autoboxing. Confira comentários sobre esta resposta para exceções à regra em que os primitivos são alocados no heap.

Em Java e C #, não precisamos alocar tipos primitivos no heap. Eles podem ser alocados na pilha (não que estejam restritos a pilha). Considerando que, em C ++, podemos ter tipos primitivos e definidos pelo usuário a serem alocados tanto na pilha quanto no heap.

Em C ++, há uma maneira adicional de usar o novo operador, e isso é por meio de “posicionamento novo”. A memory para a qual você aponta pode existir em qualquer lugar.

Veja quais usos existem para “colocação nova”?