aula 10 tipos abstractos de dados i. 2003/2004 introdução à programação 2 flashback lembram-se...

Post on 17-Apr-2015

107 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Aula 10

Tipos Abstractos de Dados I

2003/2004Introdução à Programação2

Flashback

Lembram-se da Aula 4?

2003/2004Introdução à Programação3

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)

2003/2004Introdução à Programação4

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)

2003/2004Introdução à Programação5

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.

2003/2004Introdução à Programação6

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!

2003/2004Introdução à Programação7

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)

2003/2004Introdução à Programação8

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)

2003/2004Introdução à Programação9

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; }

2003/2004Introdução à Programação10

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

2003/2004Introdução à Programação11

Objectivo

Escrever programa para somar fracções tão simples como para somar inteiros

Ou seja…

2003/2004Introdução à Programação12

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…

2003/2004Introdução à Programação13

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)

2003/2004Introdução à Programação14

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

2003/2004Introdução à Programação15

TAD Racional

/** Representa números racionais. */class Racional { public: int numerador; int denominador;};

Variáveis membro ou atributos

Variáveis membro ou atributos

Atenção ao ; final!

2003/2004Introdução à Programação16

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;};

2003/2004Introdução à Programação17

Representação gráfica do TAD

Racional

numerador: intdenominador: int

Atributos: instânciasmembro

Operações: rotinasmembro

Nome

2003/2004Introdução à Programação18

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!

2003/2004Introdução à Programação19

Representações gráficas (I)

r1: Racional

numerador = ?denominador = ?

r2: Racional

numerador = ?denominador = ?

ObjectosInstâncias do TAD

Há quem lhes chame objectos, mas reservaremos esse nome para as classes propriamente ditas.

r1: Racional

numerador = 6denominador = 9

r2: Racional

numerador = 7denominador = 3

2003/2004Introdução à Programação20

Representações gráficas (II)

r1: Racional

numerador: int

6

denominador: int

9

r2: Racional

numerador: int

7

denominador: int

3

2003/2004Introdução à Programação21

Acesso a membros de instâncias de um TAD

Operador de selecção de membro: .

instância.membro

2003/2004Introdução à Programação22

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.

2003/2004Introdução à Programação23

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.

2003/2004Introdução à Programação24

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) { …}

2003/2004Introdução à Programação25

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());

2003/2004Introdução à Programação26

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; }

2003/2004Introdução à Programação27

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)

2003/2004Introdução à Programação28

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; }

2003/2004Introdução à Programação29

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?

2003/2004Introdução à Programação30

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

2003/2004Introdução à Programação31

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

2003/2004Introdução à Programação32

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

2003/2004Introdução à Programação33

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

2003/2004Introdução à Programação34

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

2003/2004Introdução à Programação35

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ê?

2003/2004Introdução à Programação36

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

2003/2004Introdução à Programação37

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

2003/2004Introdução à Programação38

O que ainda podemos fazer...

Racional r(6, 9);r.denominador = 0;

O denominador tem de ser diferente de zero.

Como impedir acesso indevidos?

2003/2004Introdução à Programação39

Categorias de acesso

Os membros podem ser públicos (public) protegidos (protected) privados (private)

2003/2004Introdução à Programação40

Categorias de acesso

Os membros podem ser públicos (public) protegidos (protegidos (protectedprotected)) privados (private)

POO!

Acessíveis por todos

Acessíveis apenas pelos membros da classe

2003/2004Introdução à Programação41

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

2003/2004Introdução à Programação42

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; };

2003/2004Introdução à Programação43

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!

2003/2004Introdução à Programação44

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ê?

2003/2004Introdução à Programação45

Método Racional::escreve()

void Racional::escreve() { cout << numerador; if(denominador != 1) cout << '/' << denominador; }

2003/2004Introdução à Programação46

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; }

2003/2004Introdução à Programação47

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; };

2003/2004Introdução à Programação48

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.

2003/2004Introdução à Programação49

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; };

2003/2004Introdução à Programação50

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());

2003/2004Introdução à Programação51

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ê?

2003/2004Introdução à Programação52

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);}

2003/2004Introdução à Programação53

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)

2003/2004Introdução à Programação54

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!

2003/2004Introdução à Programação55

E mdc()?

Deveria passar a membro?

Porquê?

2003/2004Introdução à Programação56

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

2003/2004Introdução à Programação57

Desenho de TAD

Começar sempre:

pela interface e

pelo contrato.

2003/2004Introdução à Programação58

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)

2003/2004Introdução à Programação59

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; };

2003/2004Introdução à Programação60

Método Racional::cumpreInvariante()

bool Racional::cumpreInvariante() { return 0 < denominador and mdc(numerador, denominador) == 1;}

2003/2004Introdução à Programação61

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

private: …};

2003/2004Introdução à Programação62

Método Racional::Racional()Racional::Racional(int const n) : numerador(n), denominador(1){

assert(cumpreInvariante()); }

2003/2004Introdução à Programação63

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: …};

2003/2004Introdução à Programação64

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);}

2003/2004Introdução à Programação65

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: …};

2003/2004Introdução à Programação66

Método Racional::escreve()

void Racional::escreve() { assert(cumpreInvariante());

cout << numerador; if(denominador != 1) cout << '/' << denominador;

assert(cumpreInvariante());}

2003/2004Introdução à Programação67

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: …};

2003/2004Introdução à Programação68

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; }

2003/2004Introdução à Programação69

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: …};

2003/2004Introdução à Programação70

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());

2003/2004Introdução à Programação71

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

top related