1 / 71

Aula 10

Aula 10. Tipos Abstractos de Dados I. Flashback. Lembram-se da Aula 4?. Soma de fracções (I). #include <iostream> #include <cassert> using namespace std; /** Devolve o máximo divisor comum dos inteiros passados como argumento. @pre m ≠ 0 ou n ≠ 0. @post mdc = mdc( m , n ). */

Download Presentation

Aula 10

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Aula 10 Tipos Abstractos de Dados I

  2. Flashback Lembram-se da Aula 4? Introdução à Programação

  3. Soma de fracções (I) #include <iostream> #include <cassert> using namespace std; /** Devolve o máximo divisor comum dos inteiros passados como argumento. @pre m ≠ 0 ou n ≠ 0. @post mdc = mdc(m, n). */ int mdc(int const m, int const n)  { assert(m != 0 or n != 0); … } (continua) PC relaxada para aceitar inteiros negativos e nulos (ver folhas teóricas) Introdução à Programação

  4. Soma de fracções (II) /** Reduz a fracção recebida como argumento. @pre denominador ≠ 0 numerador = ndenominador = d. @post denominador ≠ 0  mdc(numerador, denominador ) = 1  numerador/denominador = n/d. */ void reduzFracção(int& numerador, int& denominador) { assert(denominador != 0); int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor; assert(denominador != 0); assert(mdc(numerador, denominador) == 1); } (continua) Introdução à Programação

  5. Soma de fracções (III) int n, d; cin >> n >> d; if(cin.good()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; }      reduzFracção(numerador, denominador); assert(0 < denominador); assert(mdc(numerador, denominador) == 1); assert(numerador * d == n * denominador); assert(not cin.fail()); return; } assert(cin.fail()); /** Lê do teclado uma fracção, na forma de dois inteiros sucessivos. @pre numerador = ndenominador = d. @post Se cin.good() cin tem dois inteiros n' e d' disponíveis para leitura, com d' ≠ 0, então 0 < denominador mdc(numerador, denominador) = 1  numerador/denominador = n'/d' cin.fail(), senão numerador = ndenominador = dcin.fail(). */ void lêFracção(int& numerador, int& denominador) { … } (continua) Não existia na Aula 4! Garante-se denominador positivo e representação em termos mínimos. Introdução à Programação

  6. Soma de fracções (IV) /** Soma duas fracções. @pre denominador1 ≠ 0  denominador2 ≠ 0. @post numerador/ denominador = numerador1/denominador1 + numerador2/denominador2 denominador ≠ 0  mdc(numerador, denominador) = 1. */ void somaFracção(int& numerador, int& denominador,  int const numerador1, int const denominador1,                  int const numerador2, int const denominador2) { assert(denominador1 != 0); assert(denominador2 != 0); numerador = numerador1 * denominador2 + numerador2 * denominador1; denominador = denominador1 * denominador2; reduzFracção(numerador, denominador); assert(denominador != 0); assert(mdc(numerador, denominador) == 1); } (continua) Não existia na Aula 4! Introdução à Programação

  7. Soma de fracções (V) /** Escreve uma fracção no ecrã no formato usual. @pre V. @post cout.fail() cout contém n/d (ou simplesmente n, se d = 1) em que n e d são os valores de numerador e denominador. */ void escreveFracção(int const numerador, int const denominador) { cout << numerador; if(denominador != 1) cout << '/' << denominador; } (continua) Introdução à Programação

  8. Soma de fracções (VI) int main() { // Ler fracções: cout << "Introduza duas fracções (numerador denominador): "; int n1, d1, n2, d2; lêFracção(n1, d1); lêFracção(n2, d2); if(cin.fail()) { cout << "Opps! A leitura das fracções falhou!" << endl; return 1; } (continua) Introdução à Programação

  9. Soma de fracções (VII) // Calcular fracção soma reduzida: int n, d; somaFracção(n, d, n1, d1, n2, d2); // Escrever resultado: cout << "A soma de "; escreveFracção(n1, d1); cout << " com "; escreveFracção(n2, d2); cout << " é "; escreveFracção(n, d); cout << '.' << endl; } Introdução à Programação

  10. Problemas • Dois inteiros para cada fracção • Não é possível desenvolver funções para somar fracções: • funções só devolvem um valor • Código complexo e difícil de perceber Introdução à Programação

  11. Objectivo • Escrever programa para somar fracções tão simples como para somar inteiros • Ou seja… Introdução à Programação

  12. O nosso objectivo #include <iostream> using namespace std; … int main() { cout << "Introduza duas fracções (numerador denominador): "; Racional r1, r2; cin >> r1 >> r2; if(cin.fail()) { cout << "Opps! A leitura dos racionais falhou!" << endl; return 1; } Racional r = r1 + r2; cout << "A soma de " << r1 << " com " << r2 << " é " << r << '.' << endl; } Lá chegaremos, lá chegaremos… Introdução à Programação

  13. Solução • Criar um novo tipo de dados que permita representar um número racional (fracção) com uma só instância • Ou seja, criar um Tipo Abstracto de Dados (TAD) Introdução à Programação

  14. Tipos Abstractos de Dados (TAD) • Ou Tipos de Primeira Categoria • Características: • Tipo definido pelo programador • Comporta-se como os tipos básicos • Serve para definir variáveis e constantes com que se pode operar • Representado pelas classes C++ Não confundir “classe C++” com “classe” (propriamente dita)… Pormenores só em POO  Introdução à Programação

  15. Variáveis membro ou atributos Variáveis membro ou atributos TAD Racional /** Representa números racionais. */ class Racional { public: int numerador; int denominador; }; Atenção ao ; final! Introdução à Programação

  16. TAD Racional #include <iostream> #include <cassert> using namespace std; int mdc(int const m, int const n)  { … } /** Representa números racionais. */ class Racional { public: int numerador; int denominador; }; … Introdução à Programação

  17. Racional numerador: int denominador: int Representação gráfica do TAD Nome Atributos: instâncias membro Operações: rotinas membro Introdução à Programação

  18. Utilização do TAD Racional r1; Racional r2; r1.numerador = 6; r1.denominador = 9; r2.numerador = 7; r2.denominador = 3; • Cada instância de Racional tem os seus próprios atributos! Introdução à Programação

  19. r1: Racional r1: Racional r2: Racional r2: Racional numerador = ? denominador = ? numerador = 6 denominador = 9 numerador = ? denominador = ? numerador = 7 denominador = 3 Objectos Instâncias do TAD Representações gráficas (I) Há quem lhes chame objectos, mas reservaremos esse nome para as classes propriamente ditas. Introdução à Programação

  20. r1: Racional r2: Racional numerador: int denominador: int numerador: int denominador: int 6 9 7 3 Representações gráficas (II) Introdução à Programação

  21. Acesso a membros de instâncias de um TAD • Operador de selecção de membro: . instância.membro Introdução à Programação

  22. Função somaDe() /** Devolve a soma de dois racionais. @pre r1.denominador ≠ 0  r2.denominador≠ 0. @post somaDe = r1 + r2 somaDe.denominador ≠ 0  mdc(somaDe.numerador, somaDe.denominador) = 1. */ Racional somaDe(Racional const r1, Racional const r2) { assert(r1.denominador != 0); assert(r2.denominador != 0); Racional r; r.numerador = r1.numerador * r2.denominador + r2.numerador * r1.denominador; r.denominador = r1.denominador * r2.denominador; reduz(r); assert(r.denominador != 0); assert(mdc(r.numerador, r.denominador) == 1); return r; } Nome sem sufixo Fracção: redundante dado tipo dos parâmetros. A fazer. Introdução à Programação

  23. Procedimento reduz() /** Reduz a fracção que representa o racional recebido como argumento. @pre r.denominador ≠ 0  r = r. @post r.denominador ≠ 0  mdc(r.numerador, r.denominador) = 1  r = r. */ void reduz(Racional const r) { assert(r.denominador != 0); int const divisor = mdc(r.numerador, r.denominador); r.numerador /= divisor; r.denominador /= divisor; assert(r.denominador != 0); assert(mdc(r.numerador, r.denominador) == 1); } Nome sem sufixo Fracção: redundante dado tipo dos parâmetros. Introdução à Programação

  24. Procedimento lêPara() /** Lê do teclado um racional, na forma de dois inteiros sucessivos. @pre r = r. @post Se cin.good() cin tem dois inteiros n e d disponíveis para leitura, com d <> 0, então r = n/dcin.fail()  0 < r.denominador mdc(r.numerador, r.denominador) = 1, senão r = rcin.fail(). */ void lêPara(Racional& r) { … } Introdução à Programação

  25. Procedimento lêPara() int n, d; cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { r.numerador = -n; r.denominador = -d; } else { r.numerador = n; r.denominador = d; } reduz(r); assert(0 < r.denominador); assert(mdc(r.numerador, r. denominador) == 1); assert(r.numerador * d == n * r.denominador); assert(not cin.fail()); return; } assert(cin.fail()); Introdução à Programação

  26. Procedimento escreve() /** Escreve um racional no ecrã no formato de uma fracção. @pre V. @post cout.fail() cout contém n/d (ou simplesmente n, se d = 1) em que n e d são os valores de r.numerador e r.denominador. */ void escreve(Racional const r) { cout << r.numerador; if(r.denominador != 1) cout << '/' << r.denominador; } Introdução à Programação

  27. Programa principal (I) int main() { // Ler fracções: cout << "Introduza duas fracções (numerador denominador): "; Racional r1, r2; lêPara(r1); lêPara(r2); if(cin.fail()) { cout << "Opps! A leitura dos racionais falhou!" << endl; return 1; } (continua) Introdução à Programação

  28. Programa principal (II) // Calcular racional soma: Racional r = somaDe(r1, r2); // Escrever resultado: cout << "A soma de "; escreve(r1); cout << " com "; escreve(r2); cout << " é "; escreve(r); cout << '.' << endl; } Introdução à Programação

  29. Inicialização • Para inicializar um racional: Racional a; a.numerador = 10; a.denominador = 0; • Para inicializar um inteiro: int a = 10; int a(10); Mas como inicializar um racional tão simplesmente como um inteiro? Como evitar inicializações inválidas? Introdução à Programação

  30. Rotinas membro? • Sim! Classes C++ podem ter rotinas membro! • Operação: declaração de rotina membro • Método: definição de rotina membro • Diz-se que as classes C++ têm operações que são implementadas por métodos Introdução à Programação

  31. Construtores (I) • Construir uma instância de um TAD é instanciá-lo • Durante a construção é invocada uma operação especial: um construtor • Como não definimos um construtor, o compilador forneceu um que não faz nada Introdução à Programação

  32. Construtores: declaração Construtor invocável sem argumentos: constrói racional 0/1 /** Representa números racionais. */ class Racional { public: /** Constrói racional com valor inteiro. @pre V. @post *this = n 0 < denominador mdc(numerador, denominador) = 1. */ Racional(int const n = 0); /** Constrói racional correspondente a n/d. @pre d≠ 0. @post *this = n/d 0 < denominador  mdc(numerador, denominador) = 1. */ Racional(int const n, int const d); int numerador; int denominador; }; Construtor que recebe como argumento o numerador: constrói racional n/1 Construtor que recebe como argumentos o numerador e o denominador: constrói racional n/d Introdução à Programação

  33. Construtores: implementação (I) class Racional { … }; Racional::Racional(int const n) : numerador(n), denominador(1) { assert(0 < denominador); assert(mdc(numerador, denominador) == 1); } (continua) Lista de inicializadores Prefixo identifica classe a que o método pertence Introdução à Programação

  34. Construtores: implementação (II) Racional::Racional(int const n, int const d)  { assert(d != 0); if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(*this); assert(0 < denominador); assert(mdc(numerador, denominador) == 1); assert(numerador * d == n * denominador); } Acesso directo a atributos da instância impícita Variável, ou melhor, instância implícita, ou seja, a instância que está em construção Introdução à Programação

  35. Construtores: implementação (III) if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(*this); Garante-se denominador positivo e representação em termos mínimos. Para quê? Introdução à Programação

  36. A reter... • *this: explicitação da instância implícita • Construtores: • operações com mesmo nome da classe • não têm tipo de devolução • sobrecarregáveis • Se não forem definidos construtores: • C++ fornece um sem parâmetros e que não faz nada • Atributos da instância implícita directamente acessíveis dentro de métodos • Operações declaradas dentro da classe • Métodos definidos fora da classe Introdução à Programação

  37. O que já podemos fazer Construtores invocados automaticamente Racional r1; Racional r2(6, 9); escreve(r1); escreve(r2); Aparece 0! TAD nunca têm lixo! Aparece 2/3 Introdução à Programação

  38. O que ainda podemos fazer... Racional r(6, 9); r.denominador = 0; O denominador tem de ser diferente de zero. Como impedir acesso indevidos? Introdução à Programação

  39. Categorias de acesso • Os membros podem ser • públicos (public) • protegidos (protected) • privados (private) Introdução à Programação

  40. Categorias de acesso • Os membros podem ser • públicos (public) • protegidos (protected) • privados (private) Acessíveis por todos POO! Acessíveis apenas pelos membros da classe Introdução à Programação

  41. Princípio do encapsulamento • Tudo o que pode ser privado, deve ser privado! • Regra: Todos os atributos das classes devem ser privados • Os construtores da classe foram feitos públicos: porquê? Excepção: constantes podem ocasionalmente ser públicas Introdução à Programação

  42. Atributos privados /** Representa números racionais. */ class Racional { public: /** Constrói racional com valor inteiro. @pre V. @post *this = n 0 < denominador mdc(numerador, denominador) = 1. */ Racional(int const n = 0); /** Constrói racional correspondente a n/d. @pre d≠ 0. @post *this = n/d 0 < denominador  mdc(numerador, denominador) = 1. */ Racional(int const n, int const d); private: int numerador; int denominador; }; Introdução à Programação

  43. Continua tudo a funcionar? Racional r(6, 9); escreve(r); Não tem acesso aos atributos por serem privados. Faça-se o procedimento membro! Introdução à Programação

  44. Operação Racional::escreve() /** Representa números racionais. */ class Racional { public: … /** Escreve um racional no ecrã no formato de uma fracção. @pre V. @post cout.fail() ou cout contém n/d (ou simplesmente n, se d = 1) em que n e d são os valores de numerador e denominador. */ void escreve(); private: int numerador; int denominador; }; Operação pública. Porquê? Introdução à Programação

  45. Método Racional::escreve() void Racional::escreve() { cout << numerador; if(denominador != 1) cout << '/' << denominador; } Introdução à Programação

  46. Invocação de operações • Operador de selecção de membro: . Racional r1(); Racional r2(6, 9); r1.escreve(); r2.escreve(); void Racional::escreve() { cout << numerador; if(denominador != 1) cout << '/' << denominador; } Numerador de quem? Introdução à Programação

  47. Operação Racional::somaCom() /** Representa números racionais. */ class Racional { public: … /** Devolve a soma de dois racionais. @pre denominador ≠ 0  r2.denominador≠ 0. @post somaDe = *this + r2 denominador ≠ 0  somaDe.denominador ≠ 0  mdc(somaDe.numerador, somaDe.denominador) = 1. */ Racional somaCom(Racional const r2); private: int numerador; int denominador; }; Introdução à Programação

  48. Método Racional::somaCom() Racional Racional::somaCom(Racional const r2) { assert(denominador != 0); assert(r2.denominador != 0); Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador; r.reduz(); assert(denominador != 0); assert(r.denominador != 0); assert(mdc(r.numerador, r.denominador) == 1); return r; } Soma da instância implícita com r2. Introdução à Programação

  49. Operação Racional::lê() /** Representa números racionais. */ class Racional { public: … /** Lê do teclado um racional, na forma de dois inteiros sucessivos. @pre *this = r. @post Se cin.good() cin tem dois inteiros n e d disponíveis para leitura, com d <> 0, então *this = n/dcin.fail()  0 < denominador mdc(numerador, denominador) = 1, senão *this = rcin.fail(). */ void lê(); private: int numerador; int denominador; }; Introdução à Programação

  50. Método Racional::lê() void Racional::lê() { … } int n, d; cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); assert(0 < denominador); assert(mdc(numerador, denominador) == 1); assert(numerador * d == n * denominador); assert(not cin.fail()); return; } assert(cin.fail()); Introdução à Programação

More Related