Eu tenho programado por um tempo, mas tem sido principalmente Java e C #. Na verdade, nunca tive que gerenciar minha memória sozinha. Recentemente, comecei a programar em C ++ e estou um pouco confuso sobre quando devo armazenar coisas na pilha e quando armazená-las na pilha.
Meu entendimento é que as variáveis que são acessadas com muita frequência devem ser armazenadas na pilha e nos objetos, variáveis raramente usadas e grandes estruturas de dados devem ser armazenadas na pilha. Isso está correto ou estou incorreto?
Respostas:
Não, a diferença entre pilha e pilha não é desempenho. É vida útil: qualquer variável local dentro de uma função (qualquer coisa que você não faça malloc () ou nova) fica na pilha. Ele desaparece quando você retorna da função. Se você deseja que algo viva mais do que a função que o declarou, você deve alocá-lo no heap.
Para uma compreensão mais clara do que é a pilha, chegue a ela do outro lado - em vez de tentar entender o que a pilha faz em termos de linguagem de alto nível, procure "pilha de chamadas" e "convenção de chamada" e veja o que a máquina realmente funciona quando você chama uma função. A memória do computador é apenas uma série de endereços; "heap" e "stack" são invenções do compilador.
fonte
Eu diria:
Armazene-o na pilha, se puder.
Guarde-o na pilha, se precisar.
Portanto, prefira a pilha ao heap. Algumas razões possíveis pelas quais você não pode armazenar algo na pilha são:
É possível, com compiladores sensíveis, alocar objetos de tamanho não fixo na pilha (geralmente matrizes cujo tamanho não é conhecido no tempo de compilação).
fonte
É mais sutil do que as outras respostas sugerem. Não há uma divisão absoluta entre os dados na pilha e os dados no heap com base em como você os declara. Por exemplo:
No corpo de uma função, que declara um
vector
(array dinâmico) de dez números inteiros na pilha. Mas o armazenamento gerenciado pelovector
não está na pilha.Ah, mas (as outras respostas sugerem) o tempo de vida desse armazenamento é limitado pelo tempo de vida do
vector
mesmo, que aqui é baseado em pilha, portanto, não faz diferença como ele é implementado - só podemos tratá-lo como um objeto baseado em pilha com semântica de valores.Não tão. Suponha que a função fosse:
Portanto, qualquer coisa com uma
swap
função (e qualquer tipo de valor complexo deve ter uma) pode servir como um tipo de referência recuperável para alguns dados de heap, em um sistema que garante um único proprietário desses dados.Portanto, a abordagem moderna do C ++ é nunca armazenar o endereço de dados de heap em variáveis de ponteiro local simples. Todas as alocações de heap devem estar ocultas dentro das classes.
Se você fizer isso, poderá pensar em todas as variáveis em seu programa como se fossem tipos simples de valor e esquecer completamente o heap (exceto ao escrever uma nova classe de wrapper semelhante a valor para alguns dados de heap, o que deve ser incomum) .
Você apenas precisa reter um conhecimento especial para ajudá-lo a otimizar: sempre que possível, em vez de atribuir uma variável a outra como esta:
troque-os assim:
porque é muito mais rápido e não gera exceções. O único requisito é que você não precise
b
continuar com o mesmo valor (isso vai gerara
o valor, que seria descartadoa = b
).A desvantagem é que essa abordagem obriga a retornar valores de funções por meio de parâmetros de saída em vez do valor de retorno real. Mas eles estão corrigindo isso no C ++ 0x com referências rvalue .
Nas situações mais complicadas de todas, você levaria essa idéia ao extremo geral e usaria uma classe de ponteiro inteligente como a
shared_ptr
que já está em tr1. (Embora eu argumente que, se você precisar, pode ter se mudado para o ponto ideal de aplicabilidade do Standard C ++.)fonte
Você também armazenaria um item no heap se ele precisar ser usado fora do escopo da função na qual ele é criado. Um idioma usado com objetos de pilha é chamado RAII - isso envolve o uso do objeto baseado em pilha como um wrapper para um recurso. Quando o objeto é destruído, o recurso é limpo. Os objetos baseados em pilha são mais fáceis de controlar quando você pode lançar exceções - você não precisa se preocupar em excluir um objeto baseado em heap em um manipulador de exceções. É por isso que os ponteiros brutos normalmente não são usados no C ++ moderno; você usaria um ponteiro inteligente, que pode ser um wrapper baseado em pilha para um ponteiro bruto em um objeto baseado em heap.
fonte
Para adicionar às outras respostas, também pode ser sobre desempenho, pelo menos um pouco. Não que você deva se preocupar com isso, a menos que seja relevante para você, mas:
A alocação na pilha requer a localização de um rastreamento de um bloco de memória, que não é uma operação de tempo constante (e leva alguns ciclos e sobrecarga). Isso pode ficar mais lento à medida que a memória se fragmenta e / ou você está quase usando 100% do seu espaço de endereço. Por outro lado, as alocações de pilha são operações de tempo constante, basicamente "livres".
Outra coisa a considerar (novamente, realmente importante apenas se houver um problema) é que geralmente o tamanho da pilha é fixo e pode ser muito menor que o tamanho da pilha. Portanto, se você estiver alocando objetos grandes ou muitos objetos pequenos, provavelmente desejará usar o heap; se você ficar sem espaço na pilha, o tempo de execução lançará a exceção do titular do site. Geralmente não é grande coisa, mas outra coisa a considerar.
fonte
A pilha é mais eficiente e mais fácil de gerenciar dados no escopo.
Mas o heap deve ser usado para algo maior que alguns KB (é fácil em C ++, basta criar um
boost::scoped_ptr
na pilha para manter um ponteiro na memória alocada).Considere um algoritmo recursivo que continua chamando a si próprio. É muito difícil limitar e / ou adivinhar o uso total da pilha! Enquanto na pilha, o alocador (
malloc()
ounew
) pode indicar falta de memória retornandoNULL
outhrow
ing.Fonte : Kernel Linux cuja pilha não é maior que 8 KB!
fonte
std::unique_ptr
, que deve ser preferida a qualquer biblioteca externa como o Boost (embora isso alimente as coisas com o padrão ao longo do tempo).Para garantir a integridade, você pode ler o artigo de Miro Samek sobre os problemas do uso da pilha no contexto de software incorporado .
Um monte de problemas
fonte
A escolha entre alocar na pilha ou na pilha é aquela que é feita para você, dependendo de como sua variável é alocada. Se você alocar algo dinamicamente, usando uma chamada "nova", estará alocando a partir do heap. Se você alocar algo como variável global ou como parâmetro em uma função, ele será alocado na pilha.
fonte
Na minha opinião, existem dois fatores decisivos
Eu preferiria usar a pilha na maioria dos casos, mas se você precisar acessar variáveis fora do escopo, poderá usar heap.
Para aprimorar o desempenho ao usar heaps, você também pode usar a funcionalidade para criar um bloco de heap e isso pode ajudar a obter desempenho em vez de alocar cada variável em um local de memória diferente.
fonte
provavelmente isso foi respondido muito bem. Gostaria de apontar para a série de artigos abaixo para entender melhor os detalhes de baixo nível. Alex Darby tem uma série de artigos, onde ele orienta você com um depurador. Aqui está a parte 3 sobre a pilha. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/
fonte