Esta função verifica se a condição passada como argumento é verdadeira e, caso não seja, termina o programa com uma mensagem de erro adequada.
Esta função é normalmente usada para verificar se as PC de determinada função ou procedimento se verificam.
A necessidade de definir iteradores para elementos fictícios nas listas prende-se com o processo usado para as percorrer. Como já deve ter verificado, quando se itera ao longo de uma matriz com n posições, o índice usado para iterar termina com o valor n (inválido como índice), ou com o valor -1 (inválido como índice) se se iterar em sentido inverso. Por isso faz sentido que estes elementos fictícios sejam referências válidas para um iterador.
#include <cassert>
#include <iostream>using namespace std;
// Uma classe para representar listas de elementos do tipo Item (actualmente int).
class ListaInt {
public:
// Cria-se um sinónimo para simplificar a tarefa de criar listas para
// elementos de outros tipos:
typedef int Item;// Declaração de uma classe embutida que serve para percorrer e manipular listas.
class Iterador;// A classe de iteração tem acesso às listas:
friend class Iterador;// Construtor da classe, cria uma lista vazia:
ListaInt();
// Acrescenta item no início da lista:
void poe_inicio(const Item& item);
// Retira o primeiro item da lista:
void tira_primeiro();
// Acrescenta item no fim da lista:
void poe_fim(const Item& item);
// Retira o último item da lista:
void tira_ultimo();// Insere item imediatamente antes da posição indicada pelo iterador
// i, fazendo com que o iterador continue a referenciar o mesmo item:
void insere(Iterador& i, const Item& item);
// Remove o item indicado pelo iterador i, ficando o iterador a
// referenciar o item logo após o item removido:
void remove(const Iterador& i);
// Devolve o comprimento da lista:
int comprimento() const;// Devolve um iterador referenciando o primeiro item da lista:
Iterador primeiro();
// Devolve um iterador referenciando o último item da lista:
Iterador ultimo();
// Devolve um iterador referenciando o elemento fictício depois do último
// elemento da lista:
Iterador fim();
// Devolve um iterador referenciando o elemento fictício antes do primeiro
// elemento da lista:
Iterador inicio();private:
enum{limite = 100};
// Dever-se-ia usar:
// static const int limite = 100;
// mas o Visual C++ não o permite, pois não implementa a norma do C++
// como deveria.// Matriz que guarda os itens da lista:
Item itens[limite];// Contador do número de itens na lista:
int quantos;
};// Uma classe para representar iteradores para itens de listas ListaInt:
class ListaInt::Iterador {
// Referência para a lista a que o iterador está associado:
ListaInt& lista;
// Índice do elemento referenciado:
int indice;public:
// Construtor da classe. Associa o iterador com a lista lista e põe-no
// a referenciar o seu primeiro item:
explicit Iterador(ListaInt& lista);// Incrementação prefixa de iteradores (avança para o próximo item):
Iterador& operator ++ ();
// Decrementação prefixa de iteradores (recua para o item anterior):
Iterador& operator -- ();// Devolve o item referenciado pelo iterador:
Item& item();// O operador de comparação entre itens tem acesso aos membros privados da
// classe Iterador:
friend bool operator == (const Iterador& i1,
const Iterador& i2);// A classe ListaInt tem acesso a todos os membros da classe Iterador:
friend class ListaInt;
};// Definição das funções e procedimentos membro da classe ListaInt:
ListaInt::ListaInt() : quantos(0) {
// Usa-se uma lista de inicializadores. Neste caso basta dizer que a
// lista está vazia, i.e., não tem qualquer item.
}void ListaInt::poe_inicio(const Item& item) {
// Só se pode inserir se ainda houver espaço:
assert(comprimento() != limite);// Inserir no início implica rearranjar todos os itens já na lista:
for(int i = comprimento(); i != 0; i--)
itens[i] = itens[i - 1];// Agora já há espaço:
itens[0] = item;// E convém incrementar o contador de elementos...
quantos++;
}void ListaInt::tira_primeiro() {
// ...a completar na Aula 2.
}void ListaInt::poe_fim(const Item& item) {
// ...a completar na Aula 2.
}void ListaInt::tira_ultimo() {
assert(comprimento() != 0);quantos--;
}void ListaInt::insere(Iterador& iterador, const Item& item) {
// Só se pode inserir se ainda houver espaço:
assert(comprimento() != limite);// Não se pode inserir se o iterador referir o elemento antes do primeiro:
assert(iterador.indice != -1);// Há que rearranjar todos os itens a partir do referenciado pelo iterador:
for(int i = comprimento(); i != iterador.indice; i--)
itens[i] = itens[i - 1];// Agora já há espaço (não esquecer de revalidar o iterador!):
itens[iterador.indice++] = item;// Mais um...
quantos++;
}void ListaInt::remove(const Iterador& iterador) {
// ...a completar na Aula 2.
}int ListaInt::comprimento() const {
return quantos;
}ListaInt::Iterador ListaInt::primeiro() {
// Cria-se um iterador para esta lista, que referencia logo o primeiro
// item da lista (ver construtor de ListaInt::Iterador):
Iterador iterador(*this);// Devolve-se o iterador criado:
return iterador;
}ListaInt::Iterador ListaInt::ultimo() {
// Cria-se um iterador para esta lista:
Iterador iterador(*this);// O iterador deve referenciar o último item da lista:
iterador.indice = comprimento() - 1;return iterador;
}ListaInt::Iterador ListaInt::inicio() {
Iterador iterador(*this);// O iterador deve referenciar o item fictício antes do primeiro item da lista:
iterador.indice = -1;return iterador;
}ListaInt::Iterador ListaInt::fim() {
Iterador iterador(*this);// O iterador deve referenciar o item fictício após o último item da lista:
iterador.indice = comprimento();
return iterador;
}// Definição das funções e procedimentos membro da classe ListaInt::Iterador:
ListaInt::Iterador::Iterador(ListaInt& l) : lista(l), indice(0) {
}ListaInt::Iterador& ListaInt::Iterador::operator ++ () {
// ...a completar na Aula 2.return *this;
}// Operador de incrementação sufixa. Não é membro da classe (porquê?).
ListaInt::Iterador operator ++ (ListaInt::Iterador& iterador, int) {
ListaInt::Iterador resultado = iterador;
++iterador;
return resultado;
}ListaInt::Iterador& ListaInt::Iterador::operator -- () {
// Decrementar para aquém do elemento fictício inicial é um erro:
assert(indice != -1);indice--;
return *this;
}// Também não é membro da classe:
ListaInt::Iterador operator -- (ListaInt::Iterador& iterador, int) {
ListaInt::Iterador resultado = iterador;
--iterador;
return resultado;
}// Comparam-se os índices. Esta implementação não é perfeita. Em rigor
// dever-se-iam comparar também as listas respectivas (pense nisto quando
// tiver aprendido ponteiros). Nem a igualdade nem a desigualdade são funções
// membro. Deveriam sê-lo? Argumente a favor e contra.
bool operator == (const ListaInt::Iterador& i1, const ListaInt::Iterador& i2) {
return i1.indice == i2.indice;
}bool operator != (const ListaInt::Iterador& i1, const ListaInt::Iterador& i2) {
return !(i1 == i2);
}ListaInt::Item& ListaInt::Iterador::item() {
// O item só existe se o iterador não se referir a um iterador fictício:
assert(0 <= indice && indice < lista.comprimento());return lista.itens[indice];
}// Definição do operador << para escrita de listas num canal:
// ListaInt& deveria ser const ListaInt, mas isso implica a definição de um
// Iterador para listas constantes
ostream& operator << (ostream& out, ListaInt& l) {
out << '(';for(ListaInt::Iterador i = l.primeiro(); i != l.fim(); i++) {
out << i.item();
if(i != l.ultimo())
out << ", ";
}
return out << ')';
}// E finalmente um programa de teste:
int main()
{
ListaInt l;l.poe_fim(1);
l.poe_fim(2);
l.poe_fim(3);
l.poe_fim(4);for(ListaInt::Iterador i = l.primeiro(); i != l.fim(); ++i)
cout << i.item() << ' ';
cout << endl;for(ListaInt::Iterador j = l.ultimo(); j != l.inicio(); --j)
cout << j.item() << ' ';
cout << endl;cout << l << endl;
ListaInt::Iterador k = l.primeiro();
++k;l.insere(k, 10);
cout << l << endl;
l.remove(k);
cout << l << endl;
l.tira_primeiro();
cout << l << endl;l.tira_ultimo();
cout << l << endl;l.tira_primeiro();
cout << l << endl;l.tira_ultimo();
cout << l << endl;l.tira_ultimo();
cout << l << endl;
}
Use o main() do código acima para criar um programa de teste para esta classe (este programa deve encontrar-se num ficheiro diferente dos usados para definir a lista).
2.a) Faça uma classe Aluno para representar as notas de um aluno a uma cadeira. Pretende-se registar o número e nome do aluno e as notas. As notas reflectem o sistema de avaliação que se segue: ou se faz um trabalho (50%) e uma frequência (50%) ou, alternativamente, um exame. O aluno tem a nota final apurada quando tiver nota no trabalho e na frequência ou nota de exame. Se tiver as duas, a nota final é a melhor das duas.
Faça um programa de teste em que se dá o número e o nome de um aluno. Depois o programa pergunta se quer dar a nota de trabalho. Se o utilizador disser que sim o programa lê esta nota. E da mesma forma para as outras duas notas. No fim o programa indica que o aluno já tem nota final e qual ela é.
2.b) Refaça a lista de 1. para conter elementos do tipo Aluno. Assinale no .h e .cpp todas as modificações que teve que fazer. Faça um programa de teste semelhante ao de 1.
3. Considere a ListaInt feita em 1. Pretende-se fazer um iterador que permita percorrer a lista de forma ordenada. Isto é, começa por se obter um iterador para o menor valor que está na lista (ou o primeiro deles se houver mais que um igual). Depois, ao incrementar o iterador, deve-se obter o iterador para o elemento seguinte por ordem crescente. Para resolver este execício siga os passos abaixo:
3.a) Faça uma classe embutida iteradorMenor que permita percorrer todos os elementos com o menor valor da lista.
A classe ListaInt deve ser equipada de uma função membro IteradorMenor menor() que devolva um iterador para o primeiro dos elementos com o menor valor na lista. Se a lista estiver vazia o iterador deve-se referir ao elemento fictício final (depois do último). A procura deste elemento é trivial, dados os algoritmos desenvolvidos no primeiro semestre.
A incrementação de um IteradorMenor faz o seguinte: vai desde a posição dada pelo iterador antes da incrementação até ao fim da lista à procura de um valor igual. Se o encontrou, o novo valor do iterador deve-se referir a esse elemento. Caso contrário o iterador deve-se referir ao elemento fictício final.
Faça um programa de teste que lê uma série de números para um lista e escreve os menores.
3.b) Transforme a classe IteradorMenor na classe (embutida) IteradorOrdenado. A inicialização deste iterador faz-se como a do IteradorMenor. O avanço também, excepto que, se não encontrar um elemento de valor igual, deve colocar o iterador a referenciar o primeiro elemento da lista com valor imediatamente acima. Se não existir, aí sim, deve referenciar o elemento fictício final.
Faça um programa de teste que lê uma série de números para uma lista e devolve-os de forma ordenada. O ciclo de escrita dos valores deve ser:
ListaInt l;
... // leitura de valores.
// Escrita ordenada:
for(ListaInt::IteradorOrdenado i = l.menor(); i != l.fim(); ++i)
cout << l.item() << endl;
Faça um programa de teste que leia uma série de 10 alunos (número e nome)e os apresente ordenados por número.
1.b) Modifique para ordenar pelo nome.