há uma constante do tipo
int j = 20;
int f(){int const i = 10;...}
 int que tem um nome (i) e que dura
enquanto a função estiver a ser executada, i.e., que é automática,
e há uma variável do tipo  int que
tem nome (j) e que dura enquanto o programa durar, i.e., que é estática! 
Ambas são instâncias declaradas pelo programador e têm a sua
permanência controlada pelas regras da linguagem.Em C++ há também instâncias (variáveis ou constantes) não declaradas,
que não têm nome.  Por exemplo, as instâncias temporárias.  Seja a
classe C++ Racional, usada para representar a noção de número
racional.  Seja a sobrecarga do operador + para esta classe
C++:
Racional const operator + (Racional um_racional, Racional const& outro_racional)
{
um_racional += outro_racional;
return um_racional;
}
Que sucede quando se escreve:
Racional r1(1, 3);
Racional r2(2, 3);
cout << r1 + r2 << endl;
É óbvio que surge no ecrã o valor 1.  O que acontece é que o
operador + é invocado, devolvendo uma instância constante
temporária cujo valor é inserido no canal cout.  Esta
constante temporária existe enquanto a instrução estiver a ser executada e
não tem nome, pois nunca foi declarada.
Tal como com as instâncias declaradas, também as instâncias não-declaradas temporárias têm uma permanência que é dada pelas regras da linguagem.
Para além das instâncias temporárias usuais, envolvidas em praticamente todas as expressões do C++, existem outras instâncias não-declaradas mas com uma duração arbitrária, controlada directamente pelo programador. Esta instâncias chamam-se instâncias (constantes ou variáveis) dinâmicas, exactamente porque têm uma duração arbitrária.
Resumindo:
| Características: 
 | |
| Variáveis | Constantes | 
| Valor alterável | Valor fixo | 
| Declaradas | Não-declaradas (ou anónimas) | ||
| Características: 
 | Características: 
 | ||
| Automáticas | Estáticas | Temporárias | Dinâmicas | 
| Construídas quando a sua instrução de definição é atingida e destruídas no final do respectivo bloco. | Construídas no início do programa (globais) ou quando a instrução das sua definição é atingida pela primeira vez (locais) e destruídas no final do programa | Construídas durante o cálculo de uma
      expressão (e.g., para guardar valores devolvidos ou intermédios) e
      destruídas no final da expressão completa em que são criados*. * Há excepções. | Construídas e destruídas sob o domínio integral do programador. | 
Até agora só se viu como usar variáveis com nomes associados. Por exemplo, a definição
constrói uma nova variável de nome
int i = 10;
i e de tipo int
atribuindo-lhe um dado espaço na memória com valor inicial
10.  Mas o C++ permite usar instâncias sem nome e construídas
durante a execução do programa, sempre que o programador
o desejar.  Essas instâncias são construídas naquilo
a que chama a  memória livre (free store ou heap). 
Para as usar é fundamental o conceito de ponteiro, pois essas instâncias,
a que se chama  variáveis dinâmicas, não têm nenhum
nome associado, sendo por isso necessário usar endereços
de memória para as localizar.
Ao contrário das instâncias que se usaram até agora,
as instâncias dinâmicas têm uma duração
arbitrária, à escolha do programador.  Uma variável
estática global (como se viu no primeiro semestre) existe desde o início
até ao fim do programa, ou seja, é construída no
início do programa e destruída no seu fim.  Uma variável
automática existe desde o momento em que a instrução
que a define é executada (altura em que é construída)
até que é atingida a chaveta final (}) do respectivo
bloco de instruções (altura em que é destruída). 
Uma variável dinâmica é construída e destruída
quando o programador entender.
int, e guardar o seu endereço num dado ponteiro. 
Para isso usa-se o operador new:
Depois destas instruções, o ponteiro
int const* pc = new int const(10);
p passa a conter
o endereço de uma constante do tipo int com valor
inicial 10.  Esta nova constante pode ser usada como outra constante
qualquer, embora sempre através de um ponteiro, pois não
tem nome.  Por exemplo:
Da mesma forma se poderia ter construído uma variável dinâmica:
cout << *pc << endl;
int* p = new int(10);
*p = 20;
cout << *p << endl;
A construção de uma instância dinâmica é feita em dois passos distintos.
O primeiro passo é a reserva ou alocação de memória , e serve para requerer o endereço de uma zona de memória suficientemente grande para colocar uma instância do tipo pretendido. A reserva e a libertação (ver abaixo) de memória são implementadas através de uma interacção apropriada entre as bibliotecas da linguagem C++ e o sistema operativo utilizado.
O segundo passo é a construção (ou inicialização)
da instância nessa zona de memória, para o que se usa um dos
construtores do tipo pretendido.  O operador new faz os dois
passos automaticamente, muito embora eles possam ser separados.  A
separação dos dois passos só é verdadeiramente
útil em algumas ocasiões especiais (ver [1,
§10.4.11 e §19.4.4]).
delete:
É um princípio da programação em C++ que todas as variáveis dinâmicas construídas devem ser destruídas (libertadas) mais cedo ou mais tarde (de preferência mais cedo...). Mais, é boa política também que quem constrói uma variável dinâmica a deve destruir. Se é uma rotina de um módulo que constrói uma variável dinâmica, deve ser também uma rotina do mesmo módulo a destruí-la. Se é uma instância de uma classe que constrói um variável dinâmica, deve ser a mesma instância, ou pelo menos uma instância da mesma classe, a destruí-la. Quem constrói é responsável por destruir.
int const* pc = new int const(10);cout << *pc << endl;delete pc;
Existem políticas alternativas de gestão das variáveis dinâmicas:
Da mesma forma que para o operador new, o operador delete
também realiza duas operações bem distintas. 
Primeiro destrói a variável dinâmica usando o destrutor do
tipo (ver mais abaixo).  Depois liberta, "dealoca" ou devolve ao
sistema operativo a memória
por ela ocupado, que fica assim livre para posteriores reutilizações. 
É possível separar a destruição da libertação
da memória, mas mais uma vez tal prática só é
verdadeiramente útil em raras ocasiões (ver [1,
§10.4.11]).
Sempre que esta função for chamada é construído um ponteiro
void f()
{int* p = new int(0);}
p e inicializado com o endereço de uma variável
dinâmica construída através do operador new. 
Quando a função termina, a variável p é
destruída, mas a variável dinâmica mantém-se
em memória.  Se se chamar muitas vezes esta função,
a memória disponível vai-se reduzindo, pois de cada vez é ocupado
um seu pedaço para guardar uma nova variável dinâmica, à qual não há
qualquer possibilidade de acesso!  Diz-se que se
tem um "fuga de memória" (como num cano roto...).  Note-se
bem que destruir um ponteiro não destrói a variável
apontada!
Outro caso comum é a reserva de memória de que depois se perde a referência. Normalmente isso acontece por se fazer uma atribuição descuidada ao respectivo ponteiro. Por exemplo:
É de notar que algumas linguagens, como o Java, possuem uma entidade que, durante a execução do programa, se encarrega de verificar a existência de instâncias dinâmicas para as quais não sobrou qualquer referência (nenhum ponteiro com o seu endereço) e se encarregam de as libertar automaticamente (esta entidade, por razões óbvias, é conhecida por recolector de lixo ou garbage collector). Como o C++ não possui este (aparentemente) simpático mecanismo*, não se pode nunca esquecer que por cada
int const* p = new int const(11);int i;p = &i; //e perdeu-se a referência da constante dinâmica...
new deve existir um delete!
* Se feliz ou infelizmente é uma grande discussão...
double* p = new double(30.0);delete p;delete p; //erro dramático!
int* p = new int(12);*p = 20;delete p;*p = 30; //erro dramático (com sorte o programa aborta).
new como em
a instância dinâmica é inicializada usando o construtor por omissão do tipo tipo, se existir. Se esse tipo for um dos tipos básicos do C++, então a instância dinâmica não é inicializada de todo. Se o tipo não possuir um construtor por omissão, a expressão é um erro.
tipo* nome = new tipo;
É possível invocar um dos construtores do tipo explicitamente. Por exemplo, para obrigar à invocação do construtor por omissão (útil no caso de uma instância dinâmica de um tipo básico) pode-se escrever:
Em geral, pode-se invocar qualquer construtor do tipo passando-lhe os argumentos do tipo apropriado. Por exemplo:
tipo* nome = new tipo();
constrói uma variável dinâmica inteira, que inicializa com 10, e guarda o seu endereço em
int* p = new int(10);
p.  Ou, usando
a classe
Racional definida no semestre passado:
que constrói uma nova constante racional dinâmica, na memória livre, com valor 3/7.
Racional const* pr = new Racional const(6, 14);
#include <iostream>
class C {C() {std::cout << "Construtor!" << std::endl;}~C() {std::cout << "Destrutor!" << std::endl;}};
int main(){C* p = new C; //aparece "Construtor!".delete p; //aparece "Destrutor!".}
new []:
No caso das matrizes, cada elemento é construído usando o construtor por omissão (excepto para tipos básicos do C++, em que não se invoca qualquer construtor). Não se pode especificar qualquer outro construtor.
cout << "Introduza tamanho: " << endl;int n;if(cin >> n && n > 0) {int* m = new int[n];
//Utilização demcomo outra matriz qualquer:for(int i = 0; i != n; i++)m[i] = 0;}
delete []. 
Por exemplo:
O operador
Racional* mr =constrói matriz com 100 racionais com valor zero (0).
new Racional[100]; //delete[] mr; //destrói a matriz.
delete [] invoca automaticamente o destrutor
do tipo em causa para cada elemento da matriz dinâmica.
É um erro grave usar o operador delete em vez do operador
delete[]
para uma matriz dinâmica, mesmo que esta possua apenas um elemento.
new invoca uma função especial chamada
new_handler()
(que pode ser substituída pelo utilizador, se necessário). 
Por omissão, esta função lança uma excepção
(um conceito que se estudará mais tarde) chamada bad_alloc
e que tem como resultado prático abortar o programa.  A não
ser, claro, que a excepção seja explicitamente capturada
por código envolvente.  Este assunto será revisitado
mais tarde com maior cuidado.
Há uma forma alternativa do operador new (que não
se recomenda) que, caso não haja memória disponível,
se limita a devolver o valor singular dos ponteiros (o valor 0):
#include <new>...
Classe* p = new(std::nothrow) Classe;if(p == 0) {cerr << "Não havia memória..." << endl;...}
Aluno na Secção
6 do resumo da Aula 4, é possível construir uma variável
dinâmica dessa classe:
Pode-se ainda construir uma matriz com, por exemplo, 20 alunos (possível apenas porque a classe C++
Aluno* p = new Aluno("Ambrósio Amaral", 1234);cout << p->nome() << endl;delete p;
Aluno tem construtor por omissão):
Aluno* p = new Aluno[20];p[3].nome("Xisto Ximenes");delete[] p;
Suponha-se uma classe PilhaDeInt contendo uma matriz dinâmica
de itens:
Esta classe usa uma matriz dinâmica para guardar os itens na pilha. Essa matriz é construída no construtor da classe, reconstruída com maior dimensão, se a matriz estiver cheia, quando for necessário inserir mais um item na pilha e destruída no destrutor da pilha. Ou seja:
/**Representa pilhas deint.@invariant
itensaponta matriz comcapacidade_actualitens e
capacidade_inicial<=capacidade_actuale
0 <=número_de_itens<=capacidade_actual.*/
class PilhaDeInt {
public:
typedef int Item;
/**Constrói pilha vazia.@pre V.
@post
estáVazia().*/
PilhaDeInt();
/**Destrói a pilha.@pre V.
@post recursos externos reservados foram libertados.
*/
~PilhaDeInt();
/**Devolve o item que está no topo da pilha.@pre
¬estáVazia().@post
topoidêntico ao item no topo de*this.*/
Item const& topo() const;
/**Indica se a pilha está vazia.@pre V.
@post estáVazia =
*thisestá vazia.*/
bool estáVazia() const;
/**Devolve altura da pilha.@pre V.
@post
altura= altura de*this.*/
int altura() const;
/**Devolve o item que está no topo da pilha.@pre
¬estáVazia().@post
topoidêntico ao item no topo de*this.*/
Item& topo();
/**Põe um novo item na pilha (no topo).@pre V.
@post
*thiscontém um item adicional no topo igual anovo_item.*/
void põe(Item const& novo_item);
/**Tira o item que está no topo da pilha.@pre ¬
estáVazia().@post
*thiscontém os itens originais menos o do topo.*/
void tiraItem();
private:
static int const capacidade_inicial = 32;int capacidade_actual;Item* itens;int número_de_itens;
/**Indica se a pilha verifica a condição invariante da classe.@pre V.
@post
cumpreInvariante= (itens<> 0 e
capacidade_inicial<=capacidade_actuale
0 <=número_de_itens<=capacidade_actual).*/
bool cumpreInvariante() const;
};
inline PilhaDeInt::PilhaDeInt(): capacidade_actual(capacidade_inicial),
itens(new Item[capacidade_actual]),
número_de_itens(0)
{assert(cumpreInvariante());
}
inline PilhaDeInt::~PilhaDeInt()
{assert(cumpreInvariante());
delete[] itens;}
inline bool PilhaDeInt::estáVazia() const
{
assert(cumpreInvariante());
return altura() == 0;
}
inline int PilhaDeInt:: altura() const
{
assert(cumpreInvariante());
return número_de_itens;
}
inline PilhaDeInt::Item const& PilhaDeInt::topo() const
{
assert(cumpreInvariante());
assert(not estáVazia());
return itens[número_de_itens - 1];
}
inline PilhaDeInt::Item& PilhaDeInt::topo()
{
assert(cumpreInvariante());
assert(not estáVazia());
return itens[número_de_itens - 1];
}
void PilhaDeInt::põe(Item const& novo_item)
{assert(cumpreInvariante());
if(número_de_itens == capacidade_actual) {Item* const novos_itens = new Item[capacidade_actual * 2];for(int i = 0; i != número_de_itens; ++i)novos_itens[i] = itens[i];capacidade_actual *= 2;delete[] itens;itens = novos_itens;}itens[número_de_itens] = novo_item;
++número_de_itens;
assert(cumpreInvariante());
}
inline void PilhaDeInt::tiraItem()
{
assert(cumpreInvariante());
assert(not estáVazia());
--número_de_itens;
assert(cumpreInvariante());
}
inline bool PilhaDeInt::cumpreInvariante() const
{
return itens != 0 and
capacidade_inicial <= capacidade_actual and
0 <= número_de_itens <= capacidade_actual;
}
inline PilhaDeInt::PilhaDeInt(): capacidade_actual(capacidade_inicial),
itens(new Item[capacidade_actual]),
número_de_itens(0)
{assert(cumpreInvariante());
}
inline PilhaDeInt::~PilhaDeInt()
{assert(cumpreInvariante());
delete[] itens;}...
void PilhaDeInt::põe(Item const& novo_item)
{assert(cumpreInvariante());
if(número_de_itens == capacidade_actual) {Item* const novos_itens = new Item[capacidade_actual * 2];for(int i = 0; i != número_de_itens; ++i)novos_itens[i] = itens[i];capacidade_actual *= 2;delete[] itens;itens = novos_itens;}itens[número_de_itens] = novo_item;
++número_de_itens;
assert(cumpreInvariante());
}...
ListaTelefonema e ListaTelefonema::Iterador,
as alterações a fazer são relativamente poucas.
Note-se que, ao passar a usar variáveis dinâmicas (na memória
livre), pode-se relegar para o sistema a tarefa de lidar com os elos livres. 
Para isso serve o operador delete!  Logo, todo o código
relativo à cadeia dos elos livres, que era fundamental na implementação
anterior, torna-se agora desnecessário.  (Mas note-se que,
por razões técnicas, pode ser necessário mais tarde
recuperar esse código...)
Que passos é necessário dar?
Em primeiro lugar, é necessário eliminar a matriz de elos
e a constante que representava o número de máximo de itens por lista. 
Com variáveis dinâmicas o número limite de itens por
lista vai depender da memória livre disponível em cada momento. 
Por outro lado, como já não é necessário lidar
com uma cadeia de elos livres, podem-se eliminar a variável membro
primeiro_elo_livre
e os procedimentos reservaElo() e libertaElo().
Depois, há que decidir o que fazer com a
operação ListaDeInt::estáCheia().  É que agora não é fácil
saber se ainda há espaço disponível sem tentar construir
um novo elo dinâmico.  Mas, se se optar por esta solução,
não se deve destruí-lo logo em seguida, pois no tempo que
medeia entre o programador consumidor chamar a função ListaDeInt::estáCheia()
e de facto proceder a uma inserção pode deixar de haver espaço
disponível (não esquecer que os programas correm, tipicamente,
em concorrência uns com os outros, e usando a mesma memória física).  Logo, há apenas
três possíveis soluções:
false.  É a pior das soluções porque há situações em que não corresponde à verdade.ListaDeInt::estáCheia()  e que terá de ser rescrito.  Deve-se sempre pensar três
vezes antes de alterar a interface de uma classe (ou, em geral de um
módulo).ListaDeInt::estáCheia() tenta construir um novo elo da
cadeia.  Se conseguir, guarda-o para usar na próxima operação
de inserção e devolve false.  Senão
devolve true.  Note-se que há uma mudança no
contrato da classe.  Dantes, se ListaDeInt::estáCheia() devolvesse
true
era certo que a próxima operação de inserção
(se não fosse precedida por nenhuma de remoção) falharia. 
O novo contrato estabelece que se ListaDeInt::estáCheia() devolver
true
nada pode ser garantido (pois pode passar a haver memória disponível
por um outro programa concorrente ter libertado memória).new através do lançamento da excepção bad_alloc.É conveniente acrescentar um construtor à estrutura Elo. 
Esse construtor recebe como argumento um ponteiro para o elo anterior,
um ponteiro para o elo seguinte, e o valor do item a guardar nesse elo. 
Se cada um dos parâmetros do construtor tiver um valor por omissão,
simplifica-se bastante a construção de elos.
Uma vez que a cadeia de livres desapareceu, passando-se a guardar os
elos na memória livre, há que eliminar as chamadas das funções
membro reservaElo() e libertaElo() (entretanto desaparecidas),
e substitui-las por utilizações dos operadores
new e delete.
O construtor da lista pode ser bastante simplificado, uma vez que deixou
de haver cadeia de elos livres.  Por isso deve passar a ser inline. 
O construtor deve construir as guardas da cadeia duplamente ligada.
O destrutor da lista deve destruir todos os elos da cadeia, incluindo as guardas.
Há um procedimento que pode ser muito simplificado:
transfereDe(). 
Este procedimento pode passar a ser inline, pois a sua implementação
já não precisa de nenhum ciclo, limitando-se a manipular
ponteiros.
Finalmente, há que tentar perceber o que se passa quando, depois das alterações sugeridas, se copiam duas listas...
Recomenda-se a leitura dos Capítulos 4 e 5 de [2].
[2] Michael Main e Walter Savitch, "Data Structures and Other Objects Using C++", Addison-Wesley, Reading Massachusetts, 1997.