A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Aula 10 Tipos Abstractos de Dados I. 2003/2004 Introdução à Programação 2 Flashback Lembram-se da Aula 4?

Apresentações semelhantes


Apresentação em tema: "Aula 10 Tipos Abstractos de Dados I. 2003/2004 Introdução à Programação 2 Flashback Lembram-se da Aula 4?"— Transcrição da apresentação:

1 Aula 10 Tipos Abstractos de Dados I

2 2003/2004 Introdução à Programação 2 Flashback Lembram-se da Aula 4?

3 2003/2004 Introdução à Programação 3 Soma de fracções (I) #include 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)

4 2003/2004 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)

5 2003/2004 Introdução à Programação 5 Soma de fracções (III) /** 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) 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()); Não existia na Aula 4! Garante-se denominador positivo e representação em termos mínimos.

6 2003/2004 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!

7 2003/2004 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)

8 2003/2004 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)

9 2003/2004 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; }

10 2003/2004 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

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

12 2003/2004 Introdução à Programação 12 O nosso objectivo #include 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…

13 2003/2004 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)

14 2003/2004 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

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

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

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

18 2003/2004 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!

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

20 2003/2004 Introdução à Programação 20 Representações gráficas (II) r1 : Racionalnumerador : int 6 denominador : int 9 r2 : Racionalnumerador : int 7 denominador : int 3

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

22 2003/2004 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; } A fazer. Nome sem sufixo Fracção : redundante dado tipo dos parâmetros.

23 2003/2004 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.

24 2003/2004 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) { … }

25 2003/2004 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());

26 2003/2004 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; }

27 2003/2004 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)

28 2003/2004 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; }

29 2003/2004 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?

30 2003/2004 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

31 2003/2004 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

32 2003/2004 Introdução à Programação 32 Construtores: declaração /** 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 invocável sem argumentos: constrói racional 0/1 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

33 2003/2004 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

34 2003/2004 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); } Variável, ou melhor, instância implícita, ou seja, a instância que está em construção Acesso directo a atributos da instância impícita

35 2003/2004 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ê?

36 2003/2004 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

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

38 2003/2004 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?

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

40 2003/2004 Introdução à Programação 40 Categorias de acesso POO! Acessíveis por todos Acessíveis apenas pelos membros da classe

41 2003/2004 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

42 2003/2004 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; };

43 2003/2004 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!

44 2003/2004 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ê?

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

46 2003/2004 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(); Numerador de quem? void Racional::escreve() { cout << numerador; if(denominador != 1) cout << '/' << denominador; }

47 2003/2004 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; };

48 2003/2004 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.

49 2003/2004 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; };

50 2003/2004 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());

51 2003/2004 Introdução à Programação 51 Operação Racional::reduz() /** Representa números racionais. */ class Racional { public: … private: /** Reduz a fracção que representa o racional recebido como argumento. @pre denominador 0 r = r. @post denominador 0 mdc( numerador, denominador ) = 1 * this = r. */ void reduz(); int numerador; int denominador; }; Operação privada. Porquê?

52 2003/2004 Introdução à Programação 52 Método Racional::reduz() void Racional::reduz() { assert(denominador != 0); int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor; assert(denominador != 0); assert(mdc(numerador, denominador) == 1); }

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

54 2003/2004 Introdução à Programação 54 Programa principal (II) (continuação) // Calcular racional soma: Racional r = r1.somaCom(r2); // Escrever resultado: cout << "A soma de "; r1.escreve(); cout << " com "; r2.escreve(); cout << " é "; r.escreve(); cout << '.' << endl; } Horrendo!

55 2003/2004 Introdução à Programação 55 E mdc() ? Deveria passar a membro? Porquê?

56 2003/2004 Introdução à Programação 56 Classe é módulo por excelência Interface Parte pública Implementação Parte privada Métodos (implementação das operações) Manual de utilização (contrato) Comentário de documentação da classe Manual de utilização de cada operação pública

57 2003/2004 Introdução à Programação 57 Desenho de TAD Começar sempre: pela interface e pelo contrato.

58 2003/2004 Introdução à Programação 58 Condição Invariante da Classe Condição Invariante da Classe (CIC) Racional : 0 < denominador mdc( numerador, denominador ) = 1 Condição mais forte que se verifica sempre para todas as instâncias de uma classe Reflecte as assunções do produtor da classe acerca da sua implementação Objectivo: verificar erros do programador Deve verificar-se no início e no fim de cada método não- privado (no final dos construtores)

59 2003/2004 Introdução à Programação 59 Operação Racional::cumpreInvariante() /** Representa números racionais. @invariant 0 < denominador mdc( numerador, denominador ) = 1. */ class Racional { public: … private: /** Indica se a CIC se verifica. @pre V. @post cumpreInvariante = 0 < denominador mdc( numerador, denominador ) = 1. */ bool cumpreInvariante(); int numerador; int denominador; };

60 2003/2004 Introdução à Programação 60 Método Racional::cumpreInvariante() bool Racional::cumpreInvariante() { return 0 < denominador and mdc(numerador, denominador) == 1; }

61 2003/2004 Introdução à Programação 61 Operação Racional::Racional() /** … */ class Racional { public: /** Constrói racional com valor inteiro. @pre V. @post * this = n. */ Racional(int const n = 0); … private: … };

62 2003/2004 Introdução à Programação 62 Método Racional::Racional() Racional::Racional(int const n) : numerador(n), denominador(1) { assert(cumpreInvariante()); }

63 2003/2004 Introdução à Programação 63 Operação Racional::Racional() /** … */ class Racional { public: … /** Constrói racional correspondente a n / d. @pre d 0. @post * this = n / d. */ Racional(int const n, int const d); … private: … };

64 2003/2004 Introdução à Programação 64 Método Racional::Racional() Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); assert(cumpreInvariante()); assert(numerador * d == n * denominador); }

65 2003/2004 Introdução à Programação 65 Operação Racional::escreve() /** … */ 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: … };

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

67 2003/2004 Introdução à Programação 67 Operação Racional::somaCom() /** … */ class Racional { public: … /** Devolve a soma de dois racionais. @pre V. @post somaDe = * this + r2. */ Racional somaCom(Racional const r2); … private: … };

68 2003/2004 Introdução à Programação 68 Método Racional::somaCom() Racional Racional::somaCom(Racional const r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador; r.reduz(); assert(cumpreInvariante()); assert(r.cumpreInvariante()); return r; }

69 2003/2004 Introdução à Programação 69 Operação Racional::lê() /** … */ 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 (), senão * this = r cin. fail (). */ void lê(); private: … };

70 2003/2004 Introdução à Programação 70 Método Racional::lê() void Racional::lê() { … } assert(cumpreInvariante()); 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(cumpreInvariante()); assert(numerador * d == n * denominador); assert(not cin.fail()); return; } assert(cumpreInvariante()); assert(cin.fail());

71 2003/2004 Introdução à Programação 71 Aula 10: Sumário Necessidade de TAD: acrescentando tipos ao C++ Sintaxe da definição de classes C++ Sintaxe da definição de variáveis e constantes de uma classe C++: as instâncias e a instanciação Variáveis e constantes membro: instâncias membro ou atributos Acesso a atributos membro de uma classe C++: operador de selecção de membro. Rotinas membro: Operações e métodos Declaração vs. definição. A construção TAD:: Acesso a membros de uma classe C++: a instância implícita Construtores: Sintaxe Utilização Parâmetros com argumentos por omissão de novo Categorias e políticas de acesso: Membros públicos Membros privados Princípio do encapsulamento: aplicação aos TAD Noção de condição invariante de classe (CIC): regras e vantagens Exemplos com o TAD Racional, para concretização do conceito de número racional


Carregar ppt "Aula 10 Tipos Abstractos de Dados I. 2003/2004 Introdução à Programação 2 Flashback Lembram-se da Aula 4?"

Apresentações semelhantes


Anúncios Google