Pilha apropriada e uso de heap em C ++?

Eu tenho programado por um tempo, mas tem sido principalmente Java e C #. Eu nunca tive que gerenciar a memory sozinho. Eu comecei recentemente a programar em C ++ e estou um pouco confuso sobre quando devo armazenar as coisas na pilha e quando armazená-las na pilha.

Meu entendimento é que as variables ​​que são acessadas com muita freqüência devem ser armazenadas na pilha e nos objects, raramente usadas variables, e grandes estruturas de dados devem ser armazenadas no heap. Isso está correto ou estou incorreto?

Não, a diferença entre pilha e heap não é desempenho. É vida útil: qualquer variável local dentro de uma function (qualquer coisa que você não malloc () ou nova) vive na pilha. Ele desaparece quando você retorna da function. Se você quer que algo viva mais do que a function que o declarou, você deve alocá-lo no heap.

class Thingy; Thingy* foo( ) { int a; // this int lives on the stack Thingy B; // this thingy lives on the stack and will be deleted when we return from foo Thingy *pointerToB = &B; // this points to an address on the stack Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap. // pointerToC contains its address. // this is safe: C lives on the heap and outlives foo(). // Whoever you pass this to must remember to delete it! return pointerToC; // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. // whoever uses this returned pointer will probably cause a crash! return pointerToB; } 

Para um entendimento mais claro do que é a pilha, venha do outro lado – em vez de tentar entender o que a pilha faz em termos de uma linguagem de alto nível, procure por “pilha de chamadas” e “convenção de chamadas” e veja o que a máquina realmente faz quando você chama uma function. A memory do computador é apenas uma série de endereços; “heap” e “pilha” são invenções do compilador.

Eu diria:

Guarde-o na pilha, se você puder.

Guarde-o na pilha, se você precisar.

Portanto, prefira a pilha para o heap. Algumas possíveis razões pelas quais você não pode armazenar algo na pilha são:

  • É muito grande – em programas multithread no sistema operacional de 32 bits, a pilha tem um tamanho pequeno e fixo (pelo menos no tempo de criação de threads) (tipicamente apenas alguns megas). Isso é para que você possa criar muitos threads sem esgotar o endereço Para programas de 64 bits, ou single threaded (Linux, de qualquer maneira), este não é um grande problema.No Linux de 32 bits, os programas single threaded usualmente usam pilhas dinâmicas que podem continuar crescendo até atingirem o topo da pilha.
  • Você precisa acessá-lo fora do escopo do quadro de pilha original – este é realmente o principal motivo.

É possível, com compiladores sensatos, alocar objects de tamanho não fixo no heap (geralmente matrizes cujo tamanho não é conhecido em tempo de compilation).

É mais sutil do que as outras respostas sugerem. Não há divisão absoluta entre os dados na pilha e os dados no heap com base em como você os declara. Por exemplo:

 std::vector v(10); 

No corpo de uma function, isso declara um vector (matriz dinâmica) de dez inteiros na pilha. Mas o armazenamento gerenciado pelo vector não está na pilha.

Ah, mas (as outras respostas sugerem) o tempo de vida desse armazenamento é limitado pelo tempo de vida do vector si, que aqui é baseado em pilha, então não faz diferença como é implementado – nós só podemos tratá-lo como um stack-based object com semântica de valor.

Não tão. Suponha que a function fosse:

 void GetSomeNumbers(std::vector &result) { std::vector v(10); // fill v with numbers result.swap(v); } 

Portanto, qualquer coisa com uma function de swap (e qualquer tipo de valor complexo deve ter uma) pode servir como um tipo de referência reativa a alguns dados de heap, sob um sistema que garante um único proprietário desses dados.

Portanto, a abordagem moderna do C ++ é nunca armazenar o endereço dos dados do heap em variables ​​de ponteiro local nus. Todas as alocações de heap devem estar ocultas dentro das classs.

Se você fizer isso, você pode pensar em todas as variables ​​em seu programa como se fossem tipos de valor simples, e esquecer o heap completamente (exceto ao escrever uma nova class de wrapper semelhante a um valor para alguns dados de heap, que deveria ser incomum) .

Você só precisa manter um conhecimento especial para ajudá-lo a otimizar: onde possível, em vez de atribuir uma variável a outra assim:

 a = b; 

Troque-os assim:

 a.swap(b); 

porque é muito mais rápido e não gera exceções. O único requisito é que você não precise de b para continuar a manter o mesmo valor (em vez disso, ele obterá o valor de a, que seria descartado em a = b ).

A desvantagem é que essa abordagem força você a retornar valores de funções via parâmetros de saída, em vez do valor de retorno real. Mas eles estão corrigindo isso em C ++ 0x com referências rvalue .

Nas situações mais complicadas de todas, você levaria essa idéia ao extremo e usaria uma class de ponteiro inteligente, como shared_ptr que já está em tr1. (Embora eu argumente que, se você parece precisar, você possivelmente saiu do ponto de aplicação padrão do Standard C ++.)

Você também armazenaria um item no heap se ele precisar ser usado fora do escopo da function na qual ele é criado. Um idioma usado com objects de pilha é chamado RAII – isso envolve usar o object baseado em pilha como um wrapper para um recurso, quando o object é destruído, o recurso seria limpo. Objetos baseados em pilha são mais fáceis de controlar quando você está lançando exceções – você não precisa se preocupar com a exclusão de um object baseado em heap em um manipulador de exceções. É por isso que pointers brutos não são normalmente usados ​​no C ++ moderno, você usaria um ponteiro inteligente que pode ser um empacotador baseado em pilha para um ponteiro bruto para um object baseado em heap.

Para adicionar às outras respostas, também pode ser sobre o 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 no heap requer localizar um rastreamento de um bloco de memory, que não é uma operação de tempo constante (e leva alguns ciclos e sobrecarga). Isso pode ficar mais lento à medida que a memory se torna fragmentada e / ou você está chegando perto de usar 100% do seu espaço de endereçamento. Por outro lado, as alocações de pilha são constantes, basicamente operações “livres”.

Outra coisa a considerar (novamente, realmente importante apenas se se tornar um problema) é que normalmente o tamanho da pilha é fixo e pode ser muito menor que o tamanho da pilha. Portanto, se você estiver alocando objects grandes ou muitos objects pequenos, provavelmente desejará usar o heap; se você ficar sem espaço de pilha, o tempo de execução lançará a exceção de título do site. Geralmente não é grande coisa, mas outra coisa a considerar.

A pilha é mais eficiente e mais fácil de gerenciar com escopo de dados.

Mas heap deve ser usado para qualquer coisa maior que alguns KB (é fácil em C ++, basta criar um boost::scoped_ptr na pilha para manter um ponteiro para a memory alocada).

Considere um algoritmo recursivo que continue chamando por si mesmo. É muito difícil limitar ou adivinhar o uso total da pilha! Enquanto no heap, o alocador ( malloc() ou new ) pode indicar falta de memory retornando NULL ou throw .

Fonte : Kernel do Linux cuja pilha não é maior que 8KB!

Para completar, você pode ler o artigo de Miro Samek sobre os problemas de usar o heap no contexto do software embarcado .

Um monte de problemas

A escolha de alocar no heap 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 uma variável global ou como um parâmetro em uma function, ele será alocado na pilha.

Na minha opinião, existem dois fatores decisivos

 1) Scope of variable 2) Performance. 

Eu preferiria usar pilha na maioria dos casos, mas se você precisar acessar o escopo externo variável, poderá usar o heap.

Para melhorar o desempenho ao usar heaps, você também pode usar a funcionalidade para criar o bloco de heap e isso pode ajudar na obtenção de desempenho, em vez de alocar cada variável em um local de memory diferente.

provavelmente isso foi respondido muito bem. Gostaria de chamar a atenção para a série de artigos abaixo para ter uma compreensão mais profunda dos 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/