new:
e se não houver memória?  Excepção
bad_alloc
ou operador especial nothrow.delete.new[] e delete[].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...  É
automática!  E há uma variável do tipo  int que
tem nome (j) e que dura enquanto o programa durar.  É 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. 
Recordam-se da classe Racional, por nós definida?  Lembram-se
que tínhamos sobrecarregado o operador adição +?
Racional operator+(Racional um_racional, Racional const& outro_racional)
{
um_racional += outro_racional;
return um_racional;
}
Que sucede quando se escreve:
int main()
{
Racional r1(1, 3);
Racional r2(2, 3);
cout << r1 + r2 << endl;
}
É óbvio que surge no ecrã o valor 1, mas como?
Discutir e dizer que o valor devolvido pela função acima é uma variável temporária! Existe enquanto a instrução estiver a ser executada!
Melhor seria se fosse uma constante temporária...
Racional const operator+(Racional um_racional,
Racional const& outro_racional)
{
um_racional += outro_racional;
return um_racional;
}
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.
Hoje vamos ver como construir instâncias também não-declaradas mas com uma duração arbitrária, controlada directamente pelo programador. Esta instâncias chamam-se 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 | ||
| 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. | 
Se uma instância dinâmica não tem nome, como nos podemos referir a ela?
Discutir. Referir aula anterior. Concluir que se manipulam através de ponteiros.
Escrever:
O que se passa? Em
int* p = new int(10);
 p fica o endereço da variável dinâmica
criada, que é do tipo  int e tem valor 10.Explicar bem sintaxe. Falar dos argumentos do construtor. Fazer diagrama de objectos UML. Não colocar nome na variável dinâmica!
Discutir também:
int const* pc = new int const(10);
Dizer que se construiu constante dinâmica.
Escrever classe:
Como criar uma variável dinâmica do tipo
class Aluno {public:Aluno(string const& nome, int const número);
string const& nome() const;int número() const;
private:string nome_;int número_;};
Aluno::Aluno(string const& nome, int const número): nome_(nome), número_(número)
{}
string const&Aluno::nome() const
{return nome_;}
intAluno::número() const
{return número_;}
 Aluno com nome "Zé"
e número 100?
Discutir. Concluir:
E uma constante?
Aluno* pa = new Aluno("Zé", 100);
Discutir. Concluir:
Aluno const* pac = new Aluno const("Zé", 100);
Desenhar no ecrã em UML! Usar {frozen} para a constante.
Como escrever o aluno no ecrã?
Discutir. Concluir:
Como destruir uma instância dinâmica?
cout << pa->nome() << ' ' << pa->número() << endl;
A partir desse momento o ponteiro
delete pa;
delete pac;
 pa  e  pac  contêm
lixo!
Explicar que o conteúdo de pa e    pac 
não muda.  Só a sua interpretação!
Repare-se:
Qual a duração da variável dinâmica?
void f(int* p)
{cout << *p << endl;delete p;}
int main(){int* pi = new int(10);f(pi);}
Este código é exemplificativo! Não estou a defender que deve ser assim sempre! Aliás, não deve ser:
Regras:
Falar em alternativas:
Erros mais comuns:
Discutir.
for(int i = 0; i != 100000000; ++i) {int* p = new int(i);}
É uma chamada fuga de memória: instâncias dinâmicas construídas e nunca destruídas.
Fazer diagrama UML. Mostrar que também é fuga de memória.
int const* p = new int const(10);p = new int const(20);
Discutir com base em diagrama UML!
double* p = new double(1.1);delete p;delete p;
Que acontece durante um new?
Discutir: reserva de memória (espaço a ocupar pela instância) e chamada do construtor (inicialização desse espaço). Deixar muito clara a importância dos construtores.
Qual é então o problema do seguinte código?
O único construtor de
Aluno* pa = new Aluno;
 Aluno tem dois parâmetros sem
valores
por omissão!
Discutir solução.
E que acontece quando se destrói?
Explicar destrutor.  Acrescentar destrutor a Aluno.  Explicar
que   Aluno  não precisa de destrutor!  É só para
exemplificar!
Explicar sintaxe. Explicar resultado de:
class Aluno {public:Aluno(string const& nome, int const número);
~Aluno();
string const& nome() const;int número() const;
private:string nome_;int número_;};
Aluno::Aluno(string const& nome, int const número): nome_(nome), número_(número)
{}
Aluno::~Aluno()
{cout << "Arghhh! Destruiram-me!" << endl;}
string const& Aluno::nome() const
{return nome_;}
int Aluno::número() const
{return número_;}
Mas também se podem criar matrizes dinâmicas! Para isso usam-se os operadores
Aluno* pa = new Aluno;delete pa;
 new[] e  delete[]:
Uma matriz dinâmica tem de ser destruída com
int* p = new int[10];
for(int i = 0; i != 10; ++i)p[i] = i;
for(int i = 0; i != 10; ++i)cout << p[i] << endl;
delete[] p;
 delete[]!
Como são construídos os elementos da matriz? Com o construtor por omissão. Excepto se forem de tipos básicos, que não são inicializados...
Que acontece quando não há memória? O operador lança uma excepção, que é um conceito que discutiremos numa próxima aula. O resultado prático é que o programa aborta. Mas depois veremos como se pode capturar a excepção. Alternativamente pode-se usar:
Neste caso se não houver memória o ponteiro fica com o valor singular 0.
#include <new>
...int* p = new(nothrow) int(20);
Dizer para evitar este tipo de utilização!
Explicar valor singular: nenhum objecto pode ter endereço 0!
Deixar claro que é melhor a solução com excepções!
#include <new>
...int* p = new(nothrow) int(20);
if(p == 0) {cerr << "Não havia memória! Que fazer?" << endl;...
}
Começar por escrever a interface da classe PilhaDeInt
 do semestre passado:
Discutir possíveis soluções. Propor matrizes dinâmicas. Discutir solução passo a passo.
/**Representa pilhas dedouble.@invariant Dizer que não se conhece por ser questão de implementação.
*/
class PilhaDeInt {
public:
typedef double Item;
/**Constrói pilha vazia.@pre V.
@post
estaVazia().*/
PilhaDeInt();
/**Devolve o item que está no topo da pilha.@pre
¬estaVazia().@post
topoidêntico ao item no topo de*this.*/
Item const& topo() const;
/**Indica se a pilha está vazia.@pre V.
@post estaVazia =
*thisestá vazia.*/
bool estáVazia() const;
/**Devolve altura da pilha.@pre V.
@post
altura= altura de*this.*/
int altura() const;
/**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 ¬
estaVazia().@post
*thiscontém os itens originais menos o do topo.*/...
void tiraItem();
private:
bool cumpreInvariante() const;
};
Fazer construtor e
/**Representa pilhas dedouble.@invariant
itensaponta matriz comcapacidade_actualitens e
capacidade_inicial<=capacidade_actuale
0 <=número_de_itens<=capacidade_actual.*/
class PilhaDeInt {
public:
...
private:
static int const capacidade_inicial = 32;int capacidade_actual;Item* itens;int número_de_itens;
bool cumpreInvariante() const;
};...
bool PilhaDeInt::cumpreInvariante() const
{
return capacidade_inicial <= capacidade_actual and
0 <= número_de_itens <= capacidade_actual;
}
 põe().  Discutir passo a passo! 
Explicar método de crescimento!  Se o crescimento tivesse de ser feito a
cada inserção a implementação era muito ineficiente!  Assim o impacte
do redimensionamento é negligenciável (o impacte amortizado é constante, de
cerca de duas cópias por inserção, de outra forma seria de n/2 para a n-ésima inserção).
Justificar bem destrutor! Fuga de memória!
inline PilhaDeInt::PilhaDeInt(): capacidade_actual(capacidade_inicial),
itens(new Item[capacidade_actual]),
número_de_itens(0)
{
assert(cumpreInvariante());}
void PilhaDeInt::põe(Item const& novo_item)
{assert(cumpreInvariante());
if(número_de_itens == capacidade_actual) {Item* 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());
}
Quem destrói a matriz dinâmica dos itens quando uma pilha é destruída? Quem a construiu foi a pilha, quem a destrói deve ser a pilha (primeira política). Conclusão: é necessário um destrutor!
Discutir resultado de:
/**Representa pilhas dedouble.@invariant Dizer que não se conhece por ser questão de implementação.
*/
class PilhaDeInt {
public:
typedef int Item;
/**Constrói pilha vazia.@pre V.
@post
estaVazia().*/
PilhaDeInt();
/**Destrói a pilha.@pre V.
@post recursos externos reservados foram libertados.
*/
~PilhaDeInt();
...
};...
inline PilhaDeInt::~PilhaDeInt()
{assert(cumpreInvariante());
delete[] itens;}
Explicar problema. Mencionar construtor por cópia: definido pelo compilador e que se limita a copiar os atributos! Deixar solução para a próxima aula!
PilhaDeInt p;
p.põe(2);p.põe(3);p.põe(4);
PilhaDeInt cópia = p;
cópia.tira();
PilhaDeIntoutra;
outra = p;