Alocação Dinâmica - C++

Started by Dark_Side, 28 de October , 2006, 09:12:44 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

1.  Introdução
2.  Retomando
3.  Os operadores NEW e  DELETE
4.  Exemplo de alocação dinâmica
5.  Finalizando


Introdução
*****************************************

Hi,

Anteriormente, para a Darkers Zine, escrevei um pequeno artigo sobre alocação dinâmica
na linguagem C. Neste artigo, abordaremos o tema na linguagem C++.
Seria interessante que você pudesse ler o artigo anterior, pois muitos conceitos que serão abordados aqui estão melhor explicados nele.


Retomando
*****************************************

O que ocorre quando fazemos: "int x = 10;" ?

Nesta declaração de variável do tipo INT, é reservado um espaço na memória
suficientemente grande para comportar valores do tipo, que no caso atual, é 10.

O mesmo ocorre quando temos um array, por exemplo:

char meu_array[100]; // Temos um array de chars

Cada elemento(char) do array acima, possui 1 byte, como são 100 elementos, temos 100 bytes reservados.

Sabemos, entretanto, que este espaço é fixo quando declaramos variáveis e arrays da maneira acima. Sabemos ainda, que podemos definir a quantidade de memória a ser reservada em tempo de execução - Alocação Dinâmica.

Na linguagem C, temos 4 funções para trabalharmos com alocação dinâmica,
são elas:

Quote.:: malloc()  -> aloca memória;
 .:: calloc()  -> aloca memória;
 .:: realloc() -> realoca memória;
 .:: free()    -> libera memória;


Em C++, temos uma maneira mais simples de trabalharmos, existem apenas 2 operadores - NEW e DELETE.
O primeiro é utilizado para alocar memória, e o último, para liberar o espaço alocado. Em C++, não temos uma função equivalente à função realloc() em C, entretanto, podemos utilizar as funções contidas no  header "vector".
Não as abordarei neste artigo para não torná-lo extenso demais.
 


Os operadores NEW e DELETE
*****************************************

Em C++, trabalhar com alocação dinâmica é bem mais simples do que na linguagem C. O uso do operador NEW lembra a declaração de simples variáveis. Veremos isso a seguir.

#include <iostream>
 using namespace std;
 int main()
 {
 int x = 10;
 return 0;
 }

Temos um simples programa em C++ onde declaramos uma variável do tipo INT,
e atribuímos seu valor como 10. Já sabemos que para um programa deste tipo , um certo espaço na memória seria reservado para a variável "x".

Outro exemplo:

#include <iostream>
 using namespace std;
 int main()
 {
 char lol[100];
 return 0;
 }

Temos agora um exemplo de um vetor de caracteres, ou também chamado de string. Considerando que 1 CHAR ocupa 1 byte, temos 1x100 = 100 bytes reservados para o array acima.

Iremos ver simples exemplos de alocação dinâmica em C++, tomando como base os exemplos acima:

#include <iostream>
 using namespace std;
 int main()
 {
 int *x = new int;
 *x = 10; 
 delete x;
 return 0;
 }

Vamos ver o que acontece no programa acima:

int *x = new int;

Primeiramente declaramos um ponteiro para inteiro, em seguida, alocamos o espaço referente ao ocupado por um INTEIRO. Em C, poderíamos ter:

Quote-----------------------------------------------------------------------

int *x = (int*)malloc(sizeof(int)); // Espaço referente a 1 inteiro

ou ainda:

int *x = (int*)calloc(1,sizeof(int)); // Espaço referente a 1 inteiro
 
-----------------------------------------------------------------------

Retomando:

*x = 10;

Atribuímos ao ponteiro o valor 10. Por que utilizamos "*x = 10" e não
"x = 10"? Simplesmente pelo fato de que estamos trabalhando com ponteiros e não com uma variável comum do tipo INT.


// Correto para ponteiros; incorreto para simples variáveis
 *x = valor

// Incorreto para ponteiros; correto para simples variáveis
  x = valor;


Ao fim do programa, temos:

  delete x;

Liberamos o espaço alocado no ponteiro; equivale a "free(x)" na linguagem C. Basicamente, a sintaxe seria:
 
Quotedelete ptr;

    ptr:
      ponteiro previamente alocado.


O uso do operador NEW pode ser resumido da seguinte forma:

QuoteTIPO * nome = new TIPO;

 TIPO:
     tipo de variável;
 
 nome:
     nome do ponteiro;


Outros exemplos:

char * var  = new char;   // Aloca em VAR a memória ocupada por 1 CHAR
float *var = new float;   // Aloca em VAR a memória ocupada por 1 FLOAT
double *var = new double; // Aloca em VAR a memória ocupada por 1 DOUBLE

Nos exemplos acima, utilizamos alocação dinâmica com variáveis simples.
Veremos agora como utilizá-la com vetores (arrays).


#include <iostream>
 using namespace std;
 int main()
 {
 char * lol = new int[3];
 lol[0] = 10;
 lol[1] = 20;
 lol[2] = 30;
 
 delete [] lol;

 return 0;
 }

Vejamos:

int * lol = new int[3];

Declaramos um ponteiro para inteiro e alocamos 3 elementos do tipo nele.
A partir daí, podemos utilizá-lo com um array.

Considerando que alocamos 3 elementos, os índices podem variar de 0 a 2:

 lol[0] = 10;
 lol[1] = 20;
 lol[2] = 30;


Analisando o exemplo, podemos concluir que, para alocar um array dinamicamente, utilizamos:

Quotevar = new tipo[elementos];

 var:
    ponteiro para o tipo a alocar;

 tipo:
    é o tipo de variável em questão;

 elementos:
    número de elementos que serão alocados.


Finalizando:

delete [] lol;

Simplesmente liberamos o espaço alocado para os elementos. Quando alocamos um array dinamicamente, liberamos o espaço utilizado na alocação da seguinte forma:

 delete [] nome_var;

Onde nome_var é o nome do ponteiro utilizado na alocação.



QuoteRESUMO
--------------------------------------------------------

1) Para alocarmos em um ponteiro, o espaço utilizado por uma
simples variável:

TIPO * ponteiro = new TIPO;

Exemplos:

int  * x = new int;  // Aloca em "x" o espaço ocupado por 1 INT
char * x = new char; // Aloca em "x" o espaço ocupado por 1 CHAR

--------------------------------------------------------

2) Quando é alocado um espaço de uma simples variável em um ponteiro,
para liberá-lo:

delete ponteiro;

Exemplo:

int * x = new int;  // Aloca em "x" o espaço ocupado por 1 INT
delete x; // Libera o espaço alocado

--------------------------------------------------------


3) Para alocarmos um array dinamicamente:

TIPO * ponteiro = new TIPO[numero_elementos];

Exemplos:

int   * x = new int[10];   // Aloca 10 elementos do tipo INT em "x"
float * x = new float[20]; // Aloca 20 elementos do tipo FLOAT em "x"

--------------------------------------------------------

4) Para liberamos o espaço que foi alocado em um array dinamicamente:

delete [] ponteiro;

Exemplo:

int   * x = new int[10];   // Aloca 10 elementos do tipo INT em "x"
delete [] x; // Libera o espaço alocado para os elementos

--------------------------------------------------------

Se você reparar bem, nos exemplos acima, não verificamos se houve ou não sucesso na alocação de memória. Já sabemos que a memória não é infinita e que pode não existir espaço o suficiente para uma alocação.
Em C++, geralmente, quando ocorre um erro ao alocar memória o programa gera uma exceção - que é um erro gerado em tempo de execução do programa.
Quando isso ocorre, devemos tratá-la. Veja:


#include <iostream>
 using namespace std;
 int main()
 {
 try{
 char * lol = new char[100];
 delete [] lol;
 }
 catch(...)
 {
 cout << "Erro ao alocar espaco!";
 return 1;
 }
 return 0;
 }


O bloco de código dentro da instrução try é executado em modo protegido. Por protegido, me refiro à capacidade de tratar possíveis erros
gerados, inesperadamente, por tais instruções em tempo de execução.


Analisando o trecho:

try{
char * lol = new char[100];
delete [] lol;
}

Inicialmente, o programa tenta alocar, dinamicamente, um array de 100 caracteres, e em seguida, libera o espaço alocado. Se por algum motivo ocorrer algum erro neste processo, o programa irá executar o bloco de código na instrução catch:

catch(...)
{
cout << "Erro ao alocar espaco!"; // Exibe um erro
return 1;  // Encerra
}

A instrução catch(), como se percebe, é utilizada para tratar exceções. Quando utilizamos o parâmetro '...' , estamos dizendo ao programa para que trate de qualquer exceção gerada. Pode-se, contudo, introduzir o tipo de exceção a ser tratada no lugar do parâmetro.

Podemos impedir que sejam geradas exceções quando a alocação dinâmica falhar:

#include <iostream>
 using namespace std;
 int main()
 {
 char * lol = new (nothrow) char[100];
 if(lol == NULL){ // Verifica se o ponteiro é NULO
 cout << "Memoria insuficiente.";
 return 1; // Encerra
 }

 delete [] lol; // Libera espaço
 
 return 0;
 }

Quando utilizamos o parâmetro (nothrow) logo após o operador NEW, um possível erro gerado na alocação não seria tratado como uma exceção, mas um ponteiro NULO seria retornado. Note que há uma semelhança com a linguagem C, onde verificamos se o ponteiro é NULO após a alocação com as funções malloc() ou calloc().


Exemplo de alocação dinâmica
*****************************************

O programa abaixo pede a entrada de um número inteiro maior do que zero,
tenta alocar um array de inteiros com o número de elementos obtido, e em seguida, armazena e mostra valores para cada elemento escolhido pelo usuário.

exemplo.cpp

#include <iostream>
using namespace std;

int main()
{
// Variável do tipo inteiro sem sinal -> armazenar número escolhido
    unsigned int num;

    int *x = NULL; // Nosso ponteiro para inteiro que será alocado
   
    cout << "Digite um numero positivo: ";
    cin >> num; // Obtém número digitado
    if(num == 0) // Verifica se o número não é ZERO
    {
           cout << "Digite um numero maior do que zero";
           return 1; // Encerra
           }
   
    try{
// Tenta aloca um array com o número de elementos escolhido, dinamicamente.
    x = new int[num];
    }
    catch(...){ // Em caso de erro (exceção)
               cout << "Memoria insuficiente para alocar.";   
               return 1;}
     int i; // Variável para loop
     
     for(i=0;i<num;i++) // Percorre de 0 até num-1 -> índices do array
     {
     cout << "Digite um numero para o elemento " << i << ": "; // Pede a entrada de um inteiro
     cin >> x[i]; // Armazena o número no elemento "i" do array
     }
   
     cout << endl; // Quebra de linha
     
     for(i=0;i<num;i++) // Percorre novamente os índices do array
     {
     cout << "Valor do elemento " << i << ": ";
     cout << x[i] << endl; // Mostra o valor do elemento posicionando no índice "i" do array
     }
   
   
    delete [] x;  // Libera o espaço alocado para os elementos
    return 0; // FIM :)
}


Finalizando
*****************************************

Cumprindo o que eu disse, estou concluído o artigo sobre alocação dinâmica na linguagem C++. Gostaria de agradecer aos usuários que estão lendo os artigos que eu escrevo, e que caso alguém encontre alguma dificuldade para entender algo que eu tenha escrito, sinta-se livre para perguntar, xingar, etc... Lolz...
Bye.

insanity

Tinha lido seu texto alocação dinamica em C., agora vou ler esse, apesar de eu não saber quase nada de C ++

// insanity

Ðark$pawn

Continue conosco Dark_Side & Parabéns!!! Ponto Positivo... ;)

whit3_sh4rk

Dark_Side
Tem o dom cara..

Parabéns mais uma vez.. qualquer um que esteja interessado em aprender, concerteza será uma ótima fonte de estudo.

[]s

Anonymous

parabens (y)

c/c++ eh uma linguagem poderosa, mas o programador tem q tomar cuidad, pois ao contrário de java e outras linguagens (se n me engano .net tb), tem o garbage collector q faz o trabalho para vc, mas q pode gerar uma perda na eficiência e/ou perfomance

tudo é uma questão de tradeoff (ixi  :P)