Aula prática 8

Sumário

Objectivos

Os alunos no final desta aula deverão conhecer:

  1. A noção de polimorfismo (dinâmico).
  2. As noções de operação e classe polimórfica.
  3. A diferença entre ligação estática e ligação dinâmica e sua importância para a implementação do polimorfismo.
  4. A distinção entre operação e método.
  5. A noção de sobreposição como fornecimento de um método para uma operação de uma classe base.
  6. A diferença entre ocultação simples e ocultação com sobreposição.
  7. As noções de operação abstracta (ou deferida) e classe abstracta.
  8. A importância dos ponteiros ou referências para o mecanismo da ligação dinâmica.

Deverão também ser capazes de:

  1. Escrever pequenos programas usando hierarquias de classes.
  2. Definir pequenas hierarquias de classes para concretização em C++ de conceitos relacionados.
  3. Utilizar apropriadamente as operações e classes abstractas para concretizar conceitos também abstractos.

Caso os alunos sintam que os objectivos não foram atingidos na totalidade deverão concluir/repetir os exercícios desta aula autonomamente e ou recorrer aos horários de dúvidas.

Resumo

O resumo da matéria abordada nesta aula prática pode ser consultado aqui.

Material de apoio

Os ficheiros relativos a esta aula estão disponíveis no arquivo Aula8.zip.

Exercícios

1.a)  Considere o programa de teste teste_de_filme.C que escreveu como solução do exercício 2 da Aula 7.  No directório ~/POO/Aula8 encontrará esse ficheiro (teste_de_filme.C) bem como os módulos filme (filme.H, filme_impl.H e filme.C), que define a classe Filme, e outros_filmes (outros_filmes.H e outros_filmes_impl.H), que define as classes FilmeEstrangeiro e EdicaoDoRealizador.

Leia o programa atentamente.  Execute-o.  Aconteceu o que previa?  Aconteceu o desejável?

1.b)  À luz do que aprendeu sobre operações virtuais e polimorfismo, leve o programa de teste a mostrar correctamente cada um dos filmes (i.e., um filme estrangeiro deve ser mostrado como tal, incluindo a nacionalidade e o idioma).  Ou seja, pretende-se que invocações da operação mostraEm() através de ponteiros para a classe Filme, levem à execução do método mostraEm() da classe a que pertence o objecto apontado (ligação dinâmica).

1.c)  A classe Filme, tal como distribuída originalmente, tem um erro subtil.  Já pensou no que acontece se alguém construir um filme passando como argumento um uma cadeia para o nome ou um realizador que contenha o caractere '\n'?  Pense no que acontece quando o filme for guardado e, posteriormente, for carregado de ficheiro...  Corrija o problema melhorando a condição invariante das várias classes e as pré-condições dos seus construtores.


2.  É possível representar fórmulas arbitrárias de forma a que possam ser calculadas sempre que necessário.  Para isso considera-se que uma fórmula pode ser vista como um conjunto de termos, organizados em árvore.  Por exemplo, a fórmula:

1 + 2 * 3 ^ 4

pode ser vista da seguinte forma:

Esta árvore (invertida) tem uma raiz, correspondente à operação de adição, e tem sete nós.  Cada nó corresponde a um termo.  Existem nós terminais, chamados folhas, que não dependem de mais nenhum nó.  São os termos da fórmula correspondentes a valores.  Os termos correspondentes a operações são nós intermédios.

2.a)  Crie uma classe abstracta chamada Termo, que represente uma generalização de todos os possíveis termos de uma fórmula (para simplificar considere apenas valores e as operações de adição, subtracção e potenciação).  Pense nas operações que deve ser possível realizar através da classe Termo.  Considere pelo menos:

  1. Calcular o valor do termo.
  2. Devolver a representação do termo como uma cadeia de caracteres.

2.b)  Crie as classes concretas necessárias para representar a fórmula acima.

2.c)  Escreva um programa de teste em que construa a árvore de termos correspondente à fórmula acima, calcule e mostre o seu valor e, finalmente, mostre a representação da fórmula como uma cadeia de caracteres.

2.d)  Como garantir segurança face ao lançamento de excepções nesta hierarquia de classes?  E no programa de teste?


3.  Um glifo é a representação gráfica de uma qualquer entidade, normalmente um caractere.  Desenhe uma hierarquia de classes baseada na classe Glifo.  Essa hierarquia deve ter duas classes concretas: Caractere, para representar os caracteres usuais, e Sorriso, para representar sorrisos usuais na Rede.  Cada glifo deve saber quantas células do ecrã ocupa na horizontal (na vertical assume-se que ocupa apenas uma).

Considere exactamente os seguintes tipos de sorrisos (represente-os pelos números abaixo):

  1. :-)
  2. ;-)
  3. :-(
  4. :-|
  5. :-D
  6. :-*
  7. :-X
  8. :-P
  9. :o)
  10. :O)
  11. ;-(
  12. :-o
  13. :-O
  14. O:-)
  15. >:->
  16. >:-)
  17. :->
  18. 8-)
  19. :-\
  20. :-/
  21. =|:-)
  22. <:o}

Escreva um programa com o Slang++ que leia um conjunto de glifos do teclado, os mostre e os guarde numa lista.  O programa termina quando se prime ctrl-q.  A lista de glifos deve ser mostrada na íntegra sempre que se prime ctrl-r.  Os caracteres normais são introduzidos premindo as respectivas teclas.  Os sorrisos são introduzidos premindo a tecla ctrl-s, que deve fazer surgir um menu de sorrisos à escolha.  A lista de glifos deve ser mostrada completa, dividida entre tantas linhas do ecrã quantas necessárias, e nenhum glifo deve ser dividido entre linhas sucessivas do ecrã.

Use o que puder do seguinte código:

#include <Slang/slang.H>

using namespace Slang;

...

string sorrisos[] = {
    ":-)",
";-)", ":-(", ":-|", ":-D", ":-*", ":-X", ":-P", ":o)", 
   
":O)", ";-(", ":-o", ":-O", "O:-)", ">:->", ">:-)", ":->", "8-)",
    ":-\\", ":-/", "=|:-)"
, "<:o}"
};
int numero_de_sorrisos = sizeof(sorrisos) / sizeof(string);

MenuSimples menu_dos_sorrisos("Escolha um sorriso :^)", 
                              sorrisos, numero_de_sorrisos);


4.a)  Escreva um programa (usando a biblioteca Slang++) que exiba um rectângulo no ecrã.  O rectângulo deve ser branco sobre fundo azul, com borda representada por asteriscos e interior representado por pontos.  Deve ter origem na linha 10 coluna 30 e deve ter 5 linhas de altura por 30 colunas de largura.  Deve definir e usar uma classe Rectangulo com um construtor apropriado para poder representar qualquer rectângulo que se pretenda e com um método desenha() para desenhar os rectângulos no ecrã.

4.b)  Melhore o programa anterior, introduzindo uma classe Forma da qual Rectangulo deve ser derivada (usando herança pública: repare que um Rectangulo é uma Forma).  Deste modo, podemos colocar na classe Forma as responsabilidades comuns a todas as formas.  Escreva também uma classe Ponto (e um ponto também é uma forma...) que represente um simples ponto no ecrã.  Altere o programa de modo a colocar um rectângulo e um ponto num vector de ponteiros para Forma e por fim percorrer esse vector pedindo às formas apontadas para se desenharem no ecrã.