# Programar em C++/Alocação dinâmica de memória
## Alocação dinâmica de memória

Refere-se ao recurso, existente nas linguagens de programação, onde podemos deixar de reservar memória quando o programa ainda está na fase de desenvolvimento, quando não sabemos ainda de quanta memória o programa irá precisar para executar algum procedimento, e o fazemos durante a execução. Podemos alocar, ou seja, reservar uma quantidade de bytes para serem usados, de duas maneiras diferentes: Em tempo de compilação ou em tempo de execução, aqui tratamos do segundo caso.
O compilador reserva espaço na memória para todos os dados declarados explicitamente, mas se usarmos ponteiros precisamos colocar no ponteiro um endereço de memória existente. Para isto, podemos usar o endereço de uma variável definida previamente ou reservar o espaço necessário no momento que precisemos. Este espaço que precisamos reservar em tempo de execução é chamada de memória alocada dinamicamente.
Reservar dinamicamente é o caso em que não sabemos, no momento da programação, a quantidade de dados que deverão ser inseridos quando o programa já está sendo executado. Em vez de tentarmos prever um limite superior para abarcar todas as situações de uso da memória, temos a possibilidade de reservar memória de modo dinâmico. Os exemplos típicos disto são os processadores de texto, nos quais não sabemos a quantidade de caracteres que o utilizador vai escrever quando o programa estiver sendo executado. Nestes casos podemos, por exemplo, receber a quantidade de caracteres que o usuário digita e depois alocamos a quantidade de memória que precisamos para guardá-lo e depois o armazenamos para uso posterior.
O modo mais comum de alocação de memória é o de alocar certa quantidade de bytes e atribuí-la a um ponteiro, provendo um "array", ou vetor. Nos tópicos a seguir abordaremos estes e outros casos de uso de memória alocada dinamicamente.

## Operador new

Existem duas maneira de alocar memória dinamicamente em tempo de execução: Em "C" e em "C++" podemos usar as funções de alocação dinâmica de memória existentes nas bibliotecas padrões, ou usamos uma palavra chave da linguagem C++ chamada new. Tratemos, portanto, do segundo caso pois é específico da linguagem "C++".
De fato esta palavra chave é considerada um operador, sua função é reservar memória em tempo de execução do programa. Ela funciona de modo análogo às funções de alocação de memória da linguagem "C", nas quais, indicamos a quantidade de bytes que queremos alocar e as mesmas reservam o referido espaço necessário. Porém, o operador new tem uma sintaxe a ser seguida, a qual será abordada mais adiante.
Sem usar o operador "new" teremos um erro primário, criado inicialmente pela alocação de maneira incorreta. Vamos ver um exemplo:
Exemplo de alocação dinâmica de forma incorreta:
De fato, no exemplo acima, podemos ver que numTests não tem valor definido durante a compilação, neste caso o compilador reporta um erro, normalmente exigindo um valor ou uma constante declarada como valor literal. A razão da exigência de ter uma constante (ou literal) é que vamos alocar memória para o "array" no ato da compilação, e o compilador necessita saber exatamente a quantidade de memória que deve reservar… porém, se o número entre colchetes é uma variável, o compilador não sabe quanta memória deveria reservar para alocar o "array".
Reformulando o exemplo anterior agora com dados dinâmicos:
Agora conseguimos criar um "array" onde é o utilizador poderá definir o tamanho do "array" e depois colocar o valor para cada um dos elementos.
O operador "new" retorna o endereço onde começa o bloco de memória que foi reservado. Como retorna um endereço podemos colocá-lo num ponteiro. Assim, teremos um meio de manipular o conteúdo da memória alocada toda vez que mencionarmos o ponteiro.
Verificamos no exemplo o uso do ponteiro que deve ser do mesmo tipo que o tipo de variável que é alocado dinamicamente:
* Temos o termo new. Que é um operador cuja função é alocar dinamicamente memória;
* Temos o tipo da variável alocada dinamicamente: "int";
* Repare que NÃO temos o nome do "array";
* Uma vez que o "array" fica sem nome, para nos referirmos a cada elemento do mesmo teremos de usar o ponteiro.
Podemos inicializar o ponteiro de duas maneiras:
ou
Por outro lado, se quisermos criar um "array" de objetos "char" podemos proceder da forma como foi dado anteriormente:

## Operador Delete

Se realmente não necessitamos mais dos dados que estão num endereço de memória dinâmica, devemos apagá-la! Necessitamos liberar essa memória através do operador delete – este operador entrega ao sistema operacional ou sistema de gerenciamento de memória os espaços de memória reservados dinamicamente.
A sintaxe é
O operador delete não apaga o ponteiro mas sim a memória para onde o ponteiro aponta. Na verdade, como já dissemos anteriormente, a memória não é de fato apagada, ela retorna ao estado de disponível como memória livre para ser alocada novamente quando for necessário.
No caso de alocação de memória utilizando ponteiros locais a ação de criar o espaço através do operador new deve ser seguida de um delete depois que o espaço alocado não for mais necessário. Da mesma forma, se o espaço alocado continuará a ser necessário no resto do programa o endereço deverá ser mantido em ponteiro global ou deverá ser retornado para ser usado depois da execução da função.
O tempo de vida de uma variável criada dinamicamente é o tempo de execução do programa. Se fizermos um delete e tivermos um ponteiro que aponta para o endereço que não esteja mais alocado, não conseguiremos acessar nenhuma memória específica e, neste caso, acessaremos uma posição de memória qualquer, geralmente a que está armazenada dentro do ponteiro. Esse valor de memória muitas vezes é zero ou qualquer outro sem sentido, um valor que em algum momento foi criado pelo processador em qualquer operação anteriormente executada.
Outro problema comum: Se alocamos memória dinamicamente dentro de uma função usando um ponteiro local, quando a função termina, o ponteiro será destruído, mas a memória mantém-se. Assim já não teríamos maneira de acessar essa memória porque nós não temos mais o seu endereço! Além disso não teremos mais como excluí-la.
Caso não for excluída a memória dinâmica alocada com o operador new através do operador delete, o programa irá acumular memória alocada (reservada), o que levará à parada inesperada do programa por falta de memória para alocação quando outra operação new for solicitada e não haver mais espaço para alocar memória.
Temos este exemplo:
Quando a função “myfunction” é chamada a variável “av” é criada na pilha e quando a função acaba a variável é perdida. O mesmo acontece com o ponteiro pt, ele é uma variável local. ou seja quando a função acaba o ponteiro também termina e é perdido. Porém o espaço de memória alocado dinamicamente ainda existe. E agora não conseguimos apagar esse espaço porque não temos mais o ponteiro e a única maneira que tínhamos para saber onde ele estava era através do ponteiro que foi perdido quando a função foi finalizada.
Analisando o programa como um todo temos, à medida que o programa continua a executar, mais e mais memória que será perdida no espaço de alocação dinâmica controlado pelo sistema operacional. Se o programa continuar deixaremos de ter memória disponível e o programa deixará de operar. Nos sistemas operacionais atualmente em uso o programa será interrompido e uma mensagem de erro será retornada para o usuário.

### Retornando um ponteiro para uma variável local

Analisemos o código abaixo, no qual há um erro:
Neste código podemos ver como os ponteiros mal administrados podem causar falhas que podem levar o programa a abortar em tempo de execução. Como está escrito nos comentários do código, o conteúdo do vetor name deixará de ser utilizável, nem para operações de leitura ou, principalmente em operações de escrita. Neste segundo caso provocará um acesso a memória não autorizado, o programa poderá travar ou abortar.
A solução é estender o tempo de vida do ponteiro e do seu endereço destino. Uma solução possível seria tornar esse "array" global, mas existem alternativas melhores.

### Retornando um Ponteiro a uma Variável Local Estática

No código a seguir temos uma alternativa para o uso de vetores e retorno de seu conteúdo:
A diferença é que usamos a palavra static. Este modificador static, quando utilizado dentro de uma função para declarar variáveis, promove o vetor à categoria de permanente durante a execução do programa. Sendo assim, teremos como utilizar o endereço do ponteiro str tanto dentro como fora da função setName e assim evitaremos de acessar uma memória não existente.

### Retornando um Ponteiro com um valor de memória Criada Dinamicamente

Outra alternativa, talvez melhor:
Isto funciona porque o ponteiro retornado da função setname aponta para o "array" cujo tempo de vida persiste até que usemos o delete ou que o programa termine. O valor do ponteiro local name é atribuído na função main a outro ponteiro str, desta forma podemos manipular o conteúdo da memória alocada até que não precisemos mais dele.
Este é um exemplo onde diferentes ponteiros apontam para o mesmo endereço. Na verdade podemos atribuir a qualquer ponteiro o endereço alocado, isso nos dá a possibilidade de manipular os dados armazenados na memória dinamicamente alocada em qualquer local onde seu endereço seja conhecido.
Para manter a segurança do código é sempre aconselhável que tenhamos um controle rígido sobre os ponteiros e seus valores (endereços armazenados). É imprescindível que os espaços de memória apontados por ponteiros sejam eliminados (liberados), quando não forem mais necessários, para evitar que o programa continue a reservar memória e não liberar, levando ao esgotamento da memória ou crescimento desnecessário do uso de memória por parte do programa.

## Alocar dinamicamente Arrays (Vetores)

Ora o que fizemos antes com variáveis, podemos fazer com "arrays", como já introduzimos brevemente anteriormente. Vejamos um exemplo:
Notar a diferença:
Para utilizar o delete em arrays usamos o ponteiro com o valor da primeira célula do array:
A melhor maneira para alocar um "array" dinamicamente e inicializá-lo com valores é usar o loop:
Note que a segunda forma, além de mais elegante, não altera o valor do vetor e assim facilita a remoção da alocação usando o delete.

## Dangling Pointers

Quando temos um ponteiro que não tem em seu conteúdo um valor de memória válido, o qual pertence ao programa sendo executado, temos um problema grave, principalmente se isso ocorreu por engano. Isso pode ser um problema difícil de identificar, principalmente se o programa já está com um certo número de linhas considerável. Vejamos um exemplo:
Neste exemplo liberamos a memória dinâmica, mas o ponteiro continua a existir. Isto é um "bug", e muito difícil de detectar. Acontece que se essa memória for acessada ou escrita será corrompida. A melhor maneira de evitar isso é, depois do delete, fazer o ponteiro apontar para zero, fazê-lo um ponteiro nulo. Depois disso se tentarem usar o ponteiro iremos ter a "run time exception" e o "bug" poderá ser identificado
Assim, corrigindo o código anterior ficaríamos com:

## Verificar a existência de memória para dinâmica

Na alocação dinâmica temos de nos certificar de que a alocação no heap foi feita com sucesso e podemos ver isso de duas maneiras:
* Uma são as exceções (este é o método defaut)
vamos ver este caso quase no ultimo capitulo- isto é uma capítulo avançado
* nothrow, aqui no caso de não se conseguir a memória retorna um ponteiro nulo, e o programa continua.
supostamente este método pode ser tedioso para grandes projetos
Vamos ver um exemplo com o caso de nothrow
Neste simples exemplo implementamos uma forma de alocar posições de memória para um vetor de 'n' elementos, de forma que o usuário possa digitar a quantidade de elementos que ele vai digitar e depois proceda com a entrada dos valores. Veja que o número de elementos é determinado pelo próprio usuário no início do programa, logo depois de indicar a quantidade de números a ser digitada o programa pede que seja alocada a quantidade de memória necessária para guardar os números a serem digitados. Neste momento, caso algum erro de alocação ocorrer, o sistema retorna o ponteiro nulo e podemos então, abortar o prosseguimento do programa, mostrando uma mensagem de erro em seguida para que o usuário fique ciente que ocorreu um erro.