Aula 13 Tipos Abstractos de Dados IV. 2003/2004 Introdução à Programação 2 Estrutura global do...
Transcript of Aula 13 Tipos Abstractos de Dados IV. 2003/2004 Introdução à Programação 2 Estrutura global do...
Aula 13
Tipos Abstractos de Dados IV
2003/2004Introdução à Programação2
Estrutura global do programa (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) {…}
class Racional {…};
Racional::Racional(int const n) {…}
Racional::Racional(int const n, int const d) {…}
int Racional::numerador() {…}
int Racional::denominador() {…}
void Racional::escreve() {…}
(continua)(continua)
Construtores
Inspectores
2003/2004Introdução à Programação3
Estrutura global do programa (II)
(continuação)(continuação)
void Racional::lê() {…}
Racional& Racional::operator++() {…}
Racional& Racional::operator--() {…}
Racional& Racional::operator*=(Racional const& r2) {…}
Racional& Racional::operator/=(Racional const& r2) {…}
Racional& Racional::operator+=(Racional const& r2) {…}
Racional& Racional::operator-=(Racional const& r2) {…}
void Racional::reduz() {…}
bool Racional::cumpreInvariante() {…}
(continua)(continua)
Modificadores
Auxiliares
2003/2004Introdução à Programação4
Estrutura global do programa (III)
(continuação)(continuação)
/** Produto de dois racionais. @pre V. @post operator* = r1 × r2. */Racional const operator*(Racional r1, Racional const& r2) {…}
/** Divisão de dois racionais. @pre r2 ≠ 0. @post operator/ = r1 / r2. */Racional const operator/(Racional r1, Racional const& r2) {…} /** Soma de dois racionais. @pre V. @post operator+ = r1 + r2. */Racional const operator+(Racional r1, Racional const& r2) {…}
/** Subtracção de dois racionais. @pre V. @post operator- = r1 - r2. */Racional const operator-(Racional r1, Racional const& r2) {…}
(continua)(continua)
Operadores aritméticos
não-membro
2003/2004Introdução à Programação5
Estrutura global do programa (IV)
(continuação)(continuação)
/** Indica se dois racionais são iguais.
@pre V.
@post operator== = (r1 = r2). */bool operator==(Racional const& r1, Racional const& r2) {…} /** Indica se dois racionais são diferentes.
@pre V.
@post operator!= = (r1 ≠ r2). */bool operator!=(Racional const& r1, Racional const& r2) {…}
int main() {…}
Operadores de igualdade e
diferençanão-membro
2003/2004Introdução à Programação6
TAD Racional (construtores)
/** Representa números racionais.
@invariant 0 < denominador_ mdc(numerador_, denominador_) = 1. */class Racional { public: /** Constrói racional com valor inteiro.
@pre V.
@post *this = valor. */ Racional(int const valor = 0);
/** Constrói racional correspondente a numerador/denominador.
@pre denominador ≠ 0.
@post *this = numerador/denominador. */ Racional(int const numerador, int const denominador);
(continua)(continua)
2003/2004Introdução à Programação7
TAD Racional (inspectores)
(continuação)(continuação) /** Devolve numerador da fracção mínima correspondente ao racional. @pre V. @post numerador/denominador() = *this. */ int numerador();
/** Devolve denominador da fracção mínima correspondente ao racional. @pre V. @post 0 < denominador (E n : V : n/denominador = *this mdc(n, denominador) = 1). */ int denominador();
/** 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(); (continua)(continua)
2003/2004Introdução à Programação8
TAD Racional (modificadores)
(continuação)(continuação) /** 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ê();
/** Incrementa o racional. @pre *this = r. @post operator++ ≡ *this *this = r + 1. */ Racional& operator++();
/** Decrementa o racional. @pre *this = r. @post operator-- ≡ *this *this = r - 1. */ Racional& operator--(); (continua)(continua)
2003/2004Introdução à Programação9
TAD Racional (modificadores)
(continuação)(continuação) /** Multiplica por um racional. @pre *this = r. @post operator*= ≡ *this *this = r × r2. */ Racional& operator*=(Racional const& r2);
/** Divide por um racional. @pre *this = r r2 ≠ 0. @post operator/= ≡ *this *this = r / r2. */ Racional& operator/=(Racional const& r2);
/** Adiciona de um racional. @pre *this = r. @post operator+= ≡ *this *this = r + r2. */ Racional& operator+=(Racional const& r2);
/** Subtrai de um racional. @pre *this = r. @post operator-= ≡ *this *this = r - r2. */ Racional& operator-=(Racional const& r2);(continua)(continua)
2003/2004Introdução à Programação10
TAD Racional (implementação)
(continuação)(continuação) private: /** Indica se a CIC se verifica. @pre V. @post cumpreInvariante = 0 < denominador_ mdc(numerador_, denominador_) = 1. */ bool cumpreInvariante();
/** Reduz a fracção que representa o racional. @pre denominador_ ≠ 0 *this = r. @post denominador_ ≠ 0 mdc(numerador_, denominador_) = 1 *this = r. */ void reduz();
int numerador_; int denominador_; };
2003/2004Introdução à Programação11
Constantes de TAD
Possível definir constantes de TAD:
Racional const um_terço(1, 3);
Mas é necessário ter alguns cuidados:
cout << um_terço.numerador() << endl;
Porque não
Racional const um_terço(1 / 3);
?
Dá erro!
2003/2004Introdução à Programação12
Constantes de TAD
Compilador admite, por omissão, que as operações alteram a instância implícita
Se a instância implícita for constante, não se podem invocar operações??
Podem, mas apenas as operações que declararem explicitamente que não alteram a instância implícita, pois a tratam como constante
2003/2004Introdução à Programação13
Operações que garantem constância: Sintaxe
class Classe { …
tipo operação(parâmetros) const; …
};
tipo Classe::operação(parâmetros) const { …
}
2003/2004Introdução à Programação14
Operações que garantem constância: Semântica
Podem ser invocadas usando constantes:
Classe const constante;
constante.operação(argumentos);
Proibido para operações não constantes.
2003/2004Introdução à Programação15
Operações que garantem constância: Semântica
Compilador impede método de fazer alterações aos atributos:
tipo Classe::operação(parâmetros) const{ …
atributoatributo = = ……;; …
} Proibido!
2003/2004Introdução à Programação16
Operações que garantem constância: Semântica
Métodos não-constantes
*this é Classe&
Métodos constantes
*this é Classe const&
2003/2004Introdução à Programação17
TAD Racional (construtores)
/** Representa números racionais.
@invariant 0 < denominador_ mdc(numerador_, denominador_) = 1. */class Racional { public: /** Constrói racional com valor inteiro.
@pre V.
@post *this = valor. */ Racional(int const valor = 0);
/** Constrói racional correspondente a numerador/denominador.
@pre denominador ≠ 0.
@post *this = numerador/denominador. */ Racional(int const numerador, int const denominador);
(continua)(continua)
Construtores não podem ser const, pois instância implícita é por eles inicializada!
2003/2004Introdução à Programação18
TAD Racional (inspectores)
(continuação)(continuação) /** Devolve numerador da fracção mínima correspondente ao racional. @pre V. @post numerador/denominador() = *this. */ int numerador() const;
/** Devolve denominador da fracção mínima correspondente ao racional. @pre V. @post 0 < denominador (E n : V : n/denominador = *this mdc(n, denominador) = 1). */ int denominador() const;
/** 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() const; (continua)(continua)
Inspectores são sempre const!
Aos inspectores também se chama interrogações (queries)
2003/2004Introdução à Programação19
TAD Racional (modificadores)
(continuação)(continuação) /** 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ê();
/** Incrementa o racional. @pre *this = r. @post operator++ ≡ *this *this = r + 1. */ Racional& operator++();
/** Decrementa o racional. @pre *this = r. @post operator-- ≡ *this *this = r - 1. */ Racional& operator--(); (continua)(continua)
2003/2004Introdução à Programação20
TAD Racional (modificadores)
(continuação)(continuação) /** Multiplica por um racional. @pre *this = r. @post operator*= ≡ *this *this = r × r2. */ Racional& operator*=(Racional const& r2);
/** Divide por um racional. @pre *this = r r2 ≠ 0. @post operator/= ≡ *this *this = r / r2. */ Racional& operator/=(Racional const& r2);
/** Adiciona de um racional. @pre *this = r. @post operator+= ≡ *this *this = r + r2. */ Racional& operator+=(Racional const& r2);
/** Subtrai de um racional. @pre *this = r. @post operator-= ≡ *this *this = r - r2. */ Racional& operator-=(Racional const& r2);(continua)(continua)
Modificadores nunca são const!
2003/2004Introdução à Programação21
TAD Racional (implementação)
(continuação)(continuação) private: /** Indica se a CIC se verifica. @pre V. @post cumpreInvariante = 0 < denominador_ mdc(numerador_, denominador_) = 1. */ bool cumpreInvariante() const;
/** Reduz a fracção que representa o racional. @pre denominador_ ≠ 0 *this = r. @post denominador_ ≠ 0 mdc(numerador_, denominador_) = 1 *this = r. */ void reduz();
int numerador_; int denominador_; };
2003/2004Introdução à Programação22
Métodos afectados
…
int Racional::numerador() const {…}
int Racional::denominador() const {…}
void Racional::escreve() const {…}
…
bool Racional::cumpreInvariante() const {…}
…
2003/2004Introdução à Programação23
Métodos afectados
void Racional::escreve() const { assert(cumpreInvariante());
cout << numerador(); if(denominador() != 1) cout << '/' << denominador();
assert(cumpreInvariante());} Desnecessário! Num método
constante, se o invariante se verifica no início (para a instância implícita), também se verifica no fim.
(Hmmm… Há excepções. )
2003/2004Introdução à Programação24
Métodos afectados
void Racional::escreve() const { assert(cumpreInvariante());
cout << numerador(); if(denominador() != 1) cout << '/' << denominador(); }
2003/2004Introdução à Programação25
Atenção!
Utilização sistemática de const tem grandes vantagens: Obriga programador a pensar
(menos erros) Programador explicita informação acerca do
programa, que o compilador usa para detectar incoerências (erros detectados mais facilmente)
Erros ocorrem mais cedo, durante compilação (erros detectados mais cedo)
2003/2004Introdução à Programação26
Desafio
Quantas invocações de rotinas estão envolvidas em
Racional r(1, 3);
Racional s = r + 2;
ignorando asserções?
Dica:
Quando se constrói um racional à custa de outro, é invocado um construtor por cópia, fornecido automaticamente pelo compilador, e que se limita a copiar os atributos um a um.
2003/2004Introdução à Programação27
Construtor Racional::Racional()Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); assert(cumpreInvariante());
assert(numerador() == n * denominador());assert(numerador() == n * denominador());}
2003/2004Introdução à Programação28
Construtor Racional::Racional()Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
2003/2004Introdução à Programação29
Inspector Racional::numerador()int Racional::numerador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return numerador_; }
2003/2004Introdução à Programação30
Inspector Racional::denominador()int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
2003/2004Introdução à Programação31
Operador Racional::operator+=()Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
2003/2004Introdução à Programação32
Método auxiliar Racional::reduz()void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
2003/2004Introdução à Programação33
Operador Racional::operator+()Racional const operator+(Racional r1, Racional const& r2) { r1 += r2;
return r1; }
2003/2004Introdução à Programação34
Operador Racional::operator==()bool operator==(Racional const& r1, Racional const& r2) { return r1.numerador() == r2.numerador() and r1.denominador() == r2.denominador(); }
2003/2004Introdução à Programação35
Traçado
Racional r(1, 3);
Racional s = r + 2;
Número de invocações:
0
2003/2004Introdução à Programação36
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação37
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação38
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação39
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação40
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação41
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
1
2003/2004Introdução à Programação42
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
2
2003/2004Introdução à Programação43
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
2
2003/2004Introdução à Programação44
Traçado
int mdc(int const m, int const n){ …
}
Número de invocações:
3
2003/2004Introdução à Programação45
Traçado
int mdc(int const m, int const n){ …
}
Número de invocações:
3
2003/2004Introdução à Programação46
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
3
2003/2004Introdução à Programação47
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
3
2003/2004Introdução à Programação48
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
3
2003/2004Introdução à Programação49
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
3
2003/2004Introdução à Programação50
Traçado
Racional::Racional(int const n, int const d){ assert(d != 0); assert(d != 0);
if(d < 0) { numerador_ = -n; denominador_ = -d; } else { numerador_ = n; denominador_ = d; }
reduz();
assert(cumpreInvariante());assert(cumpreInvariante()); assert(numerador() * d == n * denominador());assert(numerador() * d == n * denominador());}
Número de invocações:
3
2003/2004Introdução à Programação51
Traçado
Racional r(1, 3);
Racional s = r + 2;
Número de invocações:
3
2003/2004Introdução à Programação52
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); assert(cumpreInvariante());
assert(numerador() == n * denominador());assert(numerador() == n * denominador());}
Número de invocações:
4
2003/2004Introdução à Programação53
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); assert(cumpreInvariante());
assert(numerador() == n * denominador());assert(numerador() == n * denominador());}
Número de invocações:
4
2003/2004Introdução à Programação54
Traçado
Racional r(1, 3);
Racional s = r + 2;
Número de invocações:
4
2003/2004Introdução à Programação55
Traçado
Racional const operator+(Racional r1, Racional const& r2) { r1 += r2;
return r1; }
Passagem por valor implica cópia!
Número de invocações:
5
2003/2004Introdução à Programação56
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Construtor por cópia fornecido automaticamente pelo compilador.
Número de invocações:
6
2003/2004Introdução à Programação57
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Número de invocações:
6
2003/2004Introdução à Programação58
Traçado
Racional const operator+(Racional r1, Racional const& r2) { r1 += r2;
return r1; }
Número de invocações:
6
2003/2004Introdução à Programação59
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
7
2003/2004Introdução à Programação60
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
7
2003/2004Introdução à Programação61
Traçado
int Racional::numerador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return numerador_; }
Número de invocações:
8
2003/2004Introdução à Programação62
Traçado
int Racional::numerador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return numerador_; }
Número de invocações:
8
2003/2004Introdução à Programação63
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
8
2003/2004Introdução à Programação64
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
9
2003/2004Introdução à Programação65
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
9
2003/2004Introdução à Programação66
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
9
2003/2004Introdução à Programação67
Traçado
int Racional::numerador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return numerador_; }
Número de invocações:
10
2003/2004Introdução à Programação68
Traçado
int Racional::numerador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return numerador_; }
Número de invocações:
10
2003/2004Introdução à Programação69
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
10
2003/2004Introdução à Programação70
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
11
2003/2004Introdução à Programação71
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
11
2003/2004Introdução à Programação72
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
11
2003/2004Introdução à Programação73
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
11
2003/2004Introdução à Programação74
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
12
2003/2004Introdução à Programação75
Traçado
int Racional::denominador() const{ assert(cumpreInvariante()); assert(cumpreInvariante());
return denominador_; }
Número de invocações:
12
2003/2004Introdução à Programação76
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
12
2003/2004Introdução à Programação77
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
12
2003/2004Introdução à Programação78
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
13
2003/2004Introdução à Programação79
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
13
2003/2004Introdução à Programação80
Traçado
int mdc(int const m, int const n){ …
}
Número de invocações:
14
2003/2004Introdução à Programação81
Traçado
int mdc(int const m, int const n){ …
}
Número de invocações:
14
2003/2004Introdução à Programação82
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
14
2003/2004Introdução à Programação83
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
14
2003/2004Introdução à Programação84
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
14
2003/2004Introdução à Programação85
Traçado
void Racional::reduz(){ assert(denominador_ != 0); assert(denominador_ != 0);
int const divisor = mdc(numerador_, denominador_);
numerador_ /= divisor; denominador_ /= divisor;
assert(denominador_ != 0);assert(denominador_ != 0);
assert(mdc(numerador_, denominador_) == 1);assert(mdc(numerador_, denominador_) == 1);}
Número de invocações:
14
2003/2004Introdução à Programação86
Traçado
Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante());assert(cumpreInvariante()); assert(r2.cumpreInvariante()); assert(r2.cumpreInvariante());
numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador();
reduz();
assert(cumpreInvariante()); assert(cumpreInvariante());
return *this; }
Número de invocações:
14
2003/2004Introdução à Programação87
Traçado
Racional const operator+(Racional r1, Racional const& r2) { r1 += r2;
return r1; }
Devolução por valor implica cópia!
Número de invocações:
14
2003/2004Introdução à Programação88
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Número de invocações:
15
2003/2004Introdução à Programação89
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Número de invocações:
15
2003/2004Introdução à Programação90
Traçado
Racional r(1, 3);
Racional s = r + 2;
Número de invocações:
15
2003/2004Introdução à Programação91
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Número de invocações:
16
2003/2004Introdução à Programação92
Traçado
Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_){}
Número de invocações:
16
2003/2004Introdução à Programação93
Traçado
Racional r(1, 3);
Racional s = r + 2;
// Fim!
Número de invocações:
16
2003/2004Introdução à Programação94
Conclusão
Há muito mais invocações do que suspeitávamos: 16!
Cada invocação implica: Colocar endereço de retorno na pilha Construir parâmetros na pilha Executar corpo Destruir parâmetros da pilha Construir instância de devolução (se for o caso) Retornar ao local de invocação
Ok, ok… Uma das cópias provavelmente não seria feita…
2003/2004Introdução à Programação95
Eficiência
Dados Programa passa 80% do tempo em 20% do
código 20% críticos desconhecidos a priori
Conclusões Esforços de optimização antecipados são perda
de tempo
2003/2004Introdução à Programação96
Mas…
Há hábitos que contribuem para eficiência do programa e não têm qualquer desvantagem:
1. Usar passagem de argumentos por referência constante onde apropriado
2. Usar a palavra chave inline onde apropriado
Já se lá irá, já se lá irá…
2003/2004Introdução à Programação97
Rotinas em-linha (inline)
Corpo não existe num único local, sendo executado sempre que desejado
Corpo é substituído pelo compilador em todos os locais onde a rotina é invocada
Rotinas curtas e sem ciclos devem ser em-linha!
Duas ou três linhas, digamos, excluindo asserções.
2003/2004Introdução à Programação98
Exemplo
inline int soma(int const a, int const b){ return a + b; }
int x1 = 10;int x2 = 30;int x3 = 50;int r = 0;
int main(){ r = soma(x1, x2); r = soma(r, x3); }
int x1 = 10;int x2 = 30;int x3 = 50;int r = 0;
int main(){ r = x1 + x2; r = r + x3; }
Geram mesmo código máquina!Geram mesmo código máquina!
Usar variáveis globais é má ideia! Isto é só um
exemplo!
2003/2004Introdução à Programação99
Exemplo em MAC-1
int soma(int const a, int const b){ return a + b; }
int x1 = 10;int x2 = 30;int x3 = 50;int r = 0;
int main(){ r = soma(x1, x2); r = soma(r, x3); }
jump main # Variáveis: x1 = 10 x2 = 30 x3 = 50 r = 0 # Aqui faz-se a soma:main: lodd x1 # Carrega variável x1 no acumulador. push # Coloca acumulador no topo da pilha. lodd x2 # Carrega variável x2 no acumulador. push # Coloca acumulador no topo da pilha. # Aqui a pilha tem os dois argumentos x1 e x2: call soma # Invoca a função soma. insp 2 # Repõe a pilha (limpeza da casa). # Aqui o acumulador tem o valor devolvido. stod r # Guarda o acumulador na variável r. lodd r # Carrega variável r no acumulador. push # Coloca acumulador no topo da pilha. lodd x3 # Carrega variável x3 no acumulador. push # Coloca acumulador no topo da pilha. # Aqui a pilha tem os dois argumentos r e x3: call soma # Invoca a função soma. insp 2 # Repõe a pilha (limpeza da casa). # Aqui o acumulador tem o valor devolvido. stod r # Guarda o acumulador na variável r. haltsoma: lodl 2 addl 1 retn
Sem inline (nem optimização).
compilação
2003/2004Introdução à Programação100
jump main1030500
lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
????????
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
0pc
?ac
100sp
10099989796959493
…
pilha
Instruções executadas:
0
2003/2004Introdução à Programação101
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
5pc
?ac
100sp
10099989796959493
3050
????????
…
Instruções executadas:
1
2003/2004Introdução à Programação102
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
6pc
10ac
100sp
10099989796959493
3050
????????
…
Instruções executadas:
2
2003/2004Introdução à Programação103
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
10
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
7pc
10ac
99sp
10099989796959493
3050
??????
?
…
Instruções executadas:
3
2003/2004Introdução à Programação104
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
10
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
8pc
30ac
99sp
10099989796959493
3050
??????
?
…
Instruções executadas:
4
2003/2004Introdução à Programação105
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 1addl 2retn
3010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
9pc
30ac
98sp
10099989796959493
3050
?????
?
…
Instruções executadas:
5
2003/2004Introdução à Programação106
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
20pc
30ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
6
2003/2004Introdução à Programação107
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
21pc
10ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
7
2003/2004Introdução à Programação108
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
22pc
40ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
8
2003/2004Introdução à Programação109
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
10pc
40ac
98sp
10099989796959493
3050
????
?
…
Instruções executadas:
9
2003/2004Introdução à Programação110
jump main10
0lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
11pc
40ac
100sp
10099989796959493
3050
????
?
…
Instruções executadas:
10
2003/2004Introdução à Programação111
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
12pc
40ac
100sp
10099989796959493
3050
????
?
…
Instruções executadas:
11
2003/2004Introdução à Programação112
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103010
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
13pc
40ac
100sp
10099989796959493
3050
????
?
…
Instruções executadas:
12
2003/2004Introdução à Programação113
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
14pc
40ac
99sp
10099989796959493
3050
????
?
…
Instruções executadas:
13
2003/2004Introdução à Programação114
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
103040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
15pc
50ac
99sp
10099989796959493
3050
????
?
…
Instruções executadas:
14
2003/2004Introdução à Programação115
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
105040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
16pc
50ac
98sp
10099989796959493
3050
????
?
…
Instruções executadas:
15
2003/2004Introdução à Programação116
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
20pc
50ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
16
2003/2004Introdução à Programação117
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
21pc
40ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
17
2003/2004Introdução à Programação118
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
22pc
90ac
97sp
10099989796959493
3050
????
?
…
Instruções executadas:
18
2003/2004Introdução à Programação119
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
17pc
90ac
98sp
10099989796959493
3050
????
?
…
Instruções executadas:
19
2003/2004Introdução à Programação120
jump main10
40lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
18pc
90ac
100sp
10099989796959493
3050
????
?
…
Instruções executadas:
20
2003/2004Introdução à Programação121
jump main10
90lodd x1push
lodd x2push
call somainsp 2stod rlodd rpush
lodd x3push
call somainsp2stod rhalt
lodl 2addl 1retn
175040
soma: 202122
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
10111213141516171819
19pc
90ac
100sp
10099989796959493
3050
????
?
…
Instruções executadas:
21
2003/2004Introdução à Programação122
Exemplo em MAC-1
inline int soma(int const a, int const b){ return a + b; }
int x1 = 10;int x2 = 30;int x3 = 50;int r = 0;
int main(){ r = soma(x1, x2); r = soma(r, x3); }
jump main # Variáveis: x1 = 10 x2 = 20 x3 = 30 r = 0 # Aqui faz-se a soma:main: lodd x1 # Carrega variável x1 no acumulador. addd x2 # Adiciona variável x2 ao acumulador. stod r # Guarda o acumulador na variável r. lodd r # Carrega variável r no acumulador. addd x3 # Adiciona variável x3 ao acumulador. stod r # Guarda o acumulador na variável r. halt
Com inline.
compilação
2003/2004Introdução à Programação123
jump main1030500
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
0pc
?ac
100sp
10099989796959493
…
pilha
Instruções executadas:
0
2003/2004Introdução à Programação124
jump main1030500
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
5pc
?ac
100sp
10099989796959493
…
Instruções executadas:
1
2003/2004Introdução à Programação125
jump main1030500
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
6pc
10ac
100sp
10099989796959493
…
Instruções executadas:
2
2003/2004Introdução à Programação126
jump main1030500
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
7pc
40ac
100sp
10099989796959493
…
Instruções executadas:
3
2003/2004Introdução à Programação127
jump main10305040
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
8pc
40ac
100sp
10099989796959493
…
Instruções executadas:
4
2003/2004Introdução à Programação128
jump main10305040
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
9pc
40ac
100sp
10099989796959493
…
Instruções executadas:
5
2003/2004Introdução à Programação129
jump main10305040
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
10pc
90ac
100sp
10099989796959493
…
Instruções executadas:
6
2003/2004Introdução à Programação130
jump main10305090
lodd x1addd x2stod rlodd r
addd x3stod rhalt
????????
0x1: 1x2: 2x3: 3
r: 4main: 5
6789
1011
11pc
90ac
100sp
10099989796959493
…
Instruções executadas:
7Sem inline eram 21…
2003/2004Introdução à Programação131
Comparação
Sem inline:
jump main # Variáveis: x1 = 10 x2 = 30 x3 = 50 r = 0 # Aqui faz-se a soma:main: lodd x1 # Carrega variável x1 no acumulador. push # Coloca acumulador no topo da pilha. lodd x2 # Carrega variável x2 no acumulador. push # Coloca acumulador no topo da pilha. # Aqui a pilha tem os dois argumentos x1 e x2: call soma # Invoca a função soma. insp 2 # Repõe a pilha (limpeza da casa). # Aqui o acumulador tem o valor devolvido. stod r # Guarda o acumulador na variável r. lodd r # Carrega variável r no acumulador. push # Coloca acumulador no topo da pilha. lodd x3 # Carrega variável x3 no acumulador. push # Coloca acumulador no topo da pilha. # Aqui a pilha tem os dois argumentos r e x3: call soma # Invoca a função soma. insp 2 # Repõe a pilha (limpeza da casa). # Aqui o acumulador tem o valor devolvido. stod r # Guarda o acumulador na variável r. haltsoma: lodl 2 addl 1 retn
Com inline:
jump main # Variáveis: x1 = 10 x2 = 20 x3 = 30 r = 0 # Aqui faz-se a soma:main: lodd x1 # Carrega variável x1 no acumulador. addd x2 # Adiciona variável x2 ao acumulador. stod r # Guarda o acumulador na variável r. lodd r # Carrega variável r no acumulador. addd x3 # Adiciona variável x3 ao acumulador. stod r # Guarda o acumulador na variável r. halt
Sempre mais rápido!
Sempre mais curto?
Não! Por vezes é mais longo. Depende da dimensão do código das rotinas, do número de invocações, etc.
Código demasiado longo pode tornar a execução mais lenta, pois obriga a recorrer à memória virtual.
2003/2004Introdução à Programação132
Sintaxe
Colocar a palavra chave inline antes do cabeçalho na definição da rotina:
inline tipo nome(parâmetros) … { … }
ou
inline tipo Classe::nome(parâmetros) … { … }
2003/2004Introdução à Programação133
Estrutura global do programa (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) {…}
class Racional {…};
inline Racional::Racional(int const n) {…}
inline Racional::Racional(int const n, int const d) {…}
inline int Racional::numerador() const {…}
inline int Racional::denominador() const {…}
inline Racional::escreve() const {…}
(continua)(continua)
Construtores
Inspectores
2003/2004Introdução à Programação134
Estrutura global do programa (II)
(continuação)(continuação)
void Racional::lê() {…}
inline Racional& Racional::operator++() {…}
inline Racional& Racional::operator--() {…}
inline Racional& Racional::operator*=(Racional const& r2) {…}
inline Racional& Racional::operator/=(Racional const& r2) {…}
inline Racional& Racional::operator+=(Racional const& r2) {…}
inline Racional& Racional::operator-=(Racional const& r2) {…}
inline void Racional::reduz() {…}
inline bool Racional::cumpreInvariante() const {…}
(continua)(continua)
Modificadores
Auxiliares
2003/2004Introdução à Programação135
Estrutura global do programa (III)
(continuação)(continuação)
/** Produto de dois racionais. @pre V. @post operator* = r1 × r2. */inline Racional const operator*(Racional r1, Racional const& r2) {…}
/** Divisão de dois racionais. @pre r2 ≠ 0. @post operator/ = r1 / r2. */inline Racional const operator/(Racional r1, Racional const& r2) {…} /** Soma de dois racionais. @pre V. @post operator+ = r1 + r2. */inline Racional const operator+(Racional r1, Racional const& r2) {…}
/** Subtracção de dois racionais. @pre V. @post operator- = r1 - r2. */inline Racional const operator-(Racional r1, Racional const& r2) {…}
(continua)(continua)
Operadores aritméticos
não-membro
2003/2004Introdução à Programação136
Estrutura global do programa (IV)
(continuação)(continuação)
/** Indica se dois racionais são iguais.
@pre V.
@post operator== = (r1 = r2). */inline bool operator==(Racional const& r1, Racional const& r2) {…} /** Indica se dois racionais são diferentes.
@pre V.
@post operator!= = (r1 ≠ r2). */inline bool operator!=(Racional const& r1, Racional const& r2) {…}
int main() {…}
Operadores de igualdade e
diferença
2003/2004Introdução à Programação137
Operadores em falta
Aritméticos: Unários: + e -
Relacionais: <, <=, > e >=
Incrementação: Sufixo: ++ e --
Inserção e extracção de canais: << e >>
Começamos aqui.
Fica por fazer...
2003/2004Introdução à Programação138
Incrementação prefixa
class Racional { public: …
/** Incrementa o racional. @pre *this = r. @post operator++ ≡ *this *this = r + 1. */ Racional& operator++();
…
};
Racional& Racional::operator++() { assert(cumpreInvariante());
numerador_ += denominador();
assert(cumpreInvariante());
return *this; }
2003/2004Introdução à Programação139
Incrementação sufixa
?
2003/2004Introdução à Programação140
Incrementação sufixa: a solução
/** Incrementa o racional, devolvendo o seu valor antes de incrementado. @pre r = r. @post operator++ = r r = r + 1. */inline Racional const operator++(Racional& r, int){ Racional const cópia = r; ++r;
return cópia;}
Devolve-se por valor um racional com o valor antes de incrementado, i.e., devolve-se a cópia do racional r.
Faz-se uma cópia de r antes de se incrementar, recorrendo à incrementação prefixa, já definida.
Aha! Cá está a diferença! Trata-se o operador como se fosse binário, recebendo como argumento um inteiro com valor não especificado e irrelevante.
2003/2004Introdução à Programação141
Decrementação sufixa: a solução
/** Decrementa o racional, devolvendo o seu valor antes de decrementado. @pre r = r. @post operator-- = r r = r - 1. */inline Racional const operator--(Racional& r, int){ Racional const cópia = r; --r;
return cópia;}
2003/2004Introdução à Programação142
Operador - unário: operação
/** … */class Racional { public: …
/** Devolve simétrico do racional. @pre V. @post operator- = -*this. */ Racional const operator-() const;
…
private: …};
2003/2004Introdução à Programação143
Operador - unário: método
inline Racional const Racional::operator-() const{ assert(cumpreInvariante());
Racional r;
r.numerador_ = -numerador(); r.denominador_ = denominador();
assert(r.cumpreInvariante());
return r;}
Não é fundamental usar uma rotina membro, mas permite código mais eficiente, pois pode-se mudar o sinal do numerador directamente, evitando-se invocações espúrias do redutor de fracções.
2003/2004Introdução à Programação144
Operador + unário
/** Devolve o racional. @pre V. @post operator+ ≡ r. */inline Racional const& operator+(Racional const& r){ return r;}
2003/2004Introdução à Programação145
Operador <
/** Devolve verdadeiro se o primeiro racional for menor que o segundo. @pre V. @post operator< = r1 < r2. */inline bool operator<(Racional const& r1, Racional const& r2){ return r1.numerador() * r2.denominador() < r2.numerador() * r1.denominador();}
Como 0 < r1.d 0 < r2.d,
r1.n / r1.d < r2.n / r2.d r1.n x r2.d < r2.n * r1.d
2003/2004Introdução à Programação146
Operadores <=, > e >=
Podem-se definir à custa do operador <
Como?
2003/2004Introdução à Programação147
Operador >
/** Devolve verdadeiro se o primeiro racional for maior que o segundo. @pre V. @post operator> = r1 > r2. */inline bool operator>(Racional const& r1, Racional const& r2){ return r2 < r1;}
2003/2004Introdução à Programação148
Operador <=
/** Devolve verdadeiro se o primeiro racional for menor ou igual ao segundo. @pre V. @post operator<= = r1 <= r2. */inline bool operator<=(Racional const& r1, Racional const& r2){ return not (r1 > r2);}
2003/2004Introdução à Programação149
Operador >=
/** Devolve verdadeiro se o primeiro racional for menor ou igual ao segundo. @pre V. @post operator>= = r1 >= r2. */inline bool operator>=(Racional const& r1, Racional const& r2){ return not (r1 < r2);}
2003/2004Introdução à Programação150
Outro desafio (fácil)
Definir o operador == à custa do operador <
2003/2004Introdução à Programação151
Último problema
Operador /= usa o operador !=, definido mais tarde
Melhor solução: concentrar declarações de rotinas não-membro
após definição da classe Definir rotinas não-membro mais tarde
2003/2004Introdução à Programação152
Preâmbulo e definição da classe C++
#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) {…}
class Racional {…};
2003/2004Introdução à Programação153
Declaração de rotinas não-membro (I)
/** Incrementa o racional, devolvendo o seu valor antes de incrementado.
@pre r = r.
@post operator++ = r r = r + 1. */Racional const operator++(Racional& r, int);
/** Decrementa o racional, devolvendo o seu valor antes de decrementado.
@pre r = r.
@post operator-- = r r = r - 1. */Racional const operator--(Racional& r, int);
Incrementação e decrementação
sufixas
2003/2004Introdução à Programação154
Declaração de rotinas não-membro (II)
/** Produto de dois racionais. @pre V. @post operator* = r1 × r2. */Racional const operator*(Racional r1, Racional const& r2);
/** Divisão de dois racionais. @pre r2 ≠ 0. @post operator/ = r1 / r2. */Racional const operator/(Racional r1, Racional const& r2); /** Soma de dois racionais. @pre V. @post operator+ = r1 + r2. */Racional const operator+(Racional r1, Racional const& r2);
/** Subtracção de dois racionais. @pre V. @post operator- = r1 - r2. */Racional const operator-(Racional r1, Racional const& r2);
Operadores aritméticos
binários
2003/2004Introdução à Programação155
Declaração de rotinas não-membro (III)
/** Devolve o racional.
@pre V.
@post operator+ ≡ r. */Racional const& operator+(Racional const& r);
/** Indica se dois racionais são iguais.
@pre V.
@post operator== = (r1 = r2). */bool operator==(Racional const& r1, Racional const& r2); /** Indica se dois racionais são diferentes.
@pre V.
@post operator!= = (r1 ≠ r2). */bool operator!=(Racional const& r1, Racional const& r2);
Operadores de igualdade e
diferença
Operador identidade
2003/2004Introdução à Programação156
Declaração de rotinas não-membro (IV)/** Devolve verdadeiro se o primeiro racional for menor que o segundo. @pre V. @post operator< = r1 < r2. */bool operator<(Racional const& r1, Racional const& r2);
/** Devolve verdadeiro se o primeiro racional for maior que o segundo. @pre V. @post operator> = r1 > r2. */bool operator>(Racional const& r1, Racional const& r2);
/** Devolve verdadeiro se o primeiro racional for menor ou igual ao segundo. @pre V. @post operator<= = r1 <= r2. */bool operator<=(Racional const& r1, Racional const& r2);
/** Devolve verdadeiro se o primeiro racional for maior ou igual ao segundo. @pre V. @post operator>= = r1 >= r2. */bool operator>=(Racional const& r1, Racional const& r2);
Operadores relacionais
2003/2004Introdução à Programação157
Definição de métodos (I)
inline Racional::Racional(int const n) {…}
inline Racional::Racional(int const n, int const d) {…}
inline int Racional::numerador() const {…}
inline int Racional::denominador() const {…}
inline void Racional::escreve() const {…}
inline Racional const Racional::operator-() const {…}
void Racional::lê() {…}
inline Racional& Racional::operator++() {…}
inline Racional& Racional::operator--() {…}
inline Racional& Racional::operator*=(Racional const& r2) {…}
inline Racional& Racional::operator/=(Racional const& r2) {…}
inline Racional& Racional::operator+=(Racional const& r2) {…}
inline Racional& Racional::operator-=(Racional const& r2) {…}
inline void Racional::reduz() {…}
inline bool Racional::cumpreInvariante() const {…}
(continua)(continua)
Construtores
Inspectores
Modificadores
Auxiliares
2003/2004Introdução à Programação158
Definição de rotinas não-membro
inline Racional const operator++(Racional& r, int) {…}
inline Racional const operator--(Racional& r, int) {…}
inline Racional const operator*(Racional r1, Racional const& r2) {…}
inline Racional const operator/(Racional r1, Racional const& r2) {…} inline Racional const operator+(Racional r1, Racional const& r2) {…}
inline Racional const operator-(Racional r1, Racional const& r2) {…}
inline Racional const& operator+(Racional const& r) {…}
inline bool operator==(Racional const& r1, Racional const& r2) {…} inline bool operator!=(Racional const& r1, Racional const& r2) {…}
inline bool operator<(Racional const& r1, Racional const& r2) {…}
inline bool operator>(Racional const& r1, Racional const& r2) {…}
inline bool operator<=(Racional const& r1, Racional const& r2) {…}
inline bool operator>=(Racional const& r1, Racional const& r2) {…}
int main() {…}
Operadores aritméticos
binários
Incrementação e decrementação
sufixas
Operadores de igualdade e
diferença
Operador identidade
Operadores relacionais
2003/2004Introdução à Programação159
TAD Racional (construtores)
/** Representa números racionais.
@invariant 0 < denominador_ mdc(numerador_, denominador_) = 1. */class Racional { public: /** Constrói racional com valor inteiro.
@pre V.
@post *this = valor. */ Racional(int const valor = 0);
/** Constrói racional correspondente a numerador/denominador.
@pre denominador ≠ 0.
@post *this = numerador/denominador. */ Racional(int const numerador, int const denominador);
(continua)(continua)
2003/2004Introdução à Programação160
TAD Racional (inspectores)
(continuação)(continuação)
/** Devolve numerador da fracção mínima correspondente ao racional.
@pre V.
@post numerador/denominador() = *this. */ int numerador() const;
/** Devolve denominador da fracção mínima correspondente ao racional.
@pre V.
@post 0 < denominador (E n : V : n/denominador = *this mdc(n, denominador) = 1). */ int denominador() const;
(continua)(continua)
2003/2004Introdução à Programação161
TAD Racional (inspectores)
(continuação)(continuação)
/** 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() const;
/** Devolve simétrico do racional. @pre V. @post operator- = -*this. */ Racional const operator-() const;
(continua)(continua)
2003/2004Introdução à Programação162
TAD Racional (modificadores)
(continuação)(continuação) /** 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ê();
/** Incrementa o racional. @pre *this = r. @post operator++ ≡ *this *this = r + 1. */ Racional& operator++();
/** Decrementa o racional. @pre *this = r. @post operator-- ≡ *this *this = r - 1. */ Racional& operator--(); (continua)(continua)
2003/2004Introdução à Programação163
TAD Racional (modificadores)
(continuação)(continuação) /** Multiplica por um racional. @pre *this = r. @post operator*= ≡ *this *this = r × r2. */ Racional& operator*=(Racional const& r2);
/** Divide por um racional. @pre *this = r r2 ≠ 0. @post operator/= ≡ *this *this = r / r2. */ Racional& operator/=(Racional const& r2);
/** Adiciona de um racional. @pre *this = r. @post operator+= ≡ *this *this = r + r2. */ Racional& operator+=(Racional const& r2);
/** Subtrai de um racional. @pre *this = r. @post operator-= ≡ *this *this = r - r2. */ Racional& operator-=(Racional const& r2);(continua)(continua)
2003/2004Introdução à Programação164
TAD Racional (implementação)
(continuação)(continuação) private: /** Indica se a CIC se verifica. @pre V. @post cumpreInvariante = 0 < denominador_ mdc(numerador_, denominador_) = 1. */ bool cumpreInvariante() const;
/** Reduz a fracção que representa o racional. @pre denominador_ ≠ 0 *this = r. @post denominador_ ≠ 0 mdc(numerador_, denominador_) = 1 *this = r. */ void reduz();
int numerador_; int denominador_; };
2003/2004Introdução à Programação165
Aula 13: Sumário
Definindo constantes de uma classe: Necessidade de declarar operações como constantes (que não alteram a instância
implícita). Sintaxe. Devolução por valor constante. Vantagens da utilização de const.
Evitando o mecanismo de invocação de rotinas no código máquina produzido pelo compilador:
Rotinas inline. Sintaxe. Regras de utilização. Explicação do efeito de inline sobre o código máquina produzido. Exemplo com programa em C++ e respectiva tradução para MAC-1 (ver
Arquitectura de Computadores). Quando optimizar.
Incrementação e decrementação sufixa: sobrecarga. Ordem de declaração/definição de operações/métodos e rotinas não-membro
associados a um TAD.