Aula 13 Tipos Abstractos de Dados IV 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) {…} Construtores Racional::Racional(int const n, int const d) {…} int Racional::numerador() {…} Inspectores int Racional::denominador() {…} void Racional::escreve() {…} (continua) 2 Introdução à Programação 2003/2004 Estrutura global do programa (II) (continuação) void Racional::lê() {…} Racional& Racional::operator++() {…} Racional& Racional::operator--() {…} Modificadores 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() {…} Auxiliares bool Racional::cumpreInvariante() {…} (continua) 3 Introdução à Programação 2003/2004 Estrutura global do programa (III) (continuação) /** Produto de dois racionais. @pre V. @post operator* = r1 × r2. */ Racional const operator*(Racional r1, Racional const& r2) {…} Operadores aritméticos não-membro /** 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) 4 Introdução à Programação 2003/2004 Estrutura global do programa (IV) (continuação) Operadores de igualdade e diferença não-membro /** 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() {…} 5 Introdução à Programação 2003/2004 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) 6 Introdução à Programação 2003/2004 TAD Racional (inspectores) (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) 7 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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) 8 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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) 9 Introdução à Programação 2003/2004 TAD Racional (implementaçã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_; }; 10 Introdução à Programação 2003/2004 Constantes de TAD Porque não Possível definir constantes de TAD: Racional const um_terço(1 / 3); ? Racional const um_terço(1, 3); Mas é necessário ter alguns cuidados: cout << um_terço.numerador() << endl; Dá erro! 11 Introdução à Programação 2003/2004 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 12 Introdução à Programação 2003/2004 Operações que garantem constância: Sintaxe class Classe { … tipo operação(parâmetros) const; … }; tipo Classe::operação(parâmetros) const { … } 13 Introdução à Programação 2003/2004 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. 14 Introdução à Programação 2003/2004 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 { … atributo = …; … } 15 Proibido! Introdução à Programação 2003/2004 Operações que garantem constância: Semântica Métodos não-constantes Métodos constantes 16 *this é Classe& *this é Classe const& Introdução à Programação 2003/2004 TAD Racional (construtores) /** Representa números racionais. @invariant 0 < denominador_ mdc(numerador_, denominador_) = 1. */ class Racional { public: /** Constrói racional com valor inteiro. Construtores não podem ser @pre V. const, pois instância implícita é @post *this = valor. */ por eles inicializada! 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) 17 Introdução à Programação 2003/2004 TAD Racional (inspectores) (continuação) /** Devolve numerador da fracção mínima correspondente ao racional. @pre V. @post numerador/denominador() = *this. */ int numerador() const; /** DevolveInspectores denominador da fracção mínima correspondente ao racional. são sempre const! @pre V. @post 0 < denominadorAos (Einspectores n : V : n/denominador = *this também mdc(n, denominador) = 1). */ se chama interrogações int denominador() const; (queries) /** 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) 18 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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) 19 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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. Modificadores nunca são const! @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) 20 Introdução à Programação 2003/2004 TAD Racional (implementaçã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_; }; 21 Introdução à Programação 2003/2004 Métodos afectados … int Racional::numerador() const {…} int Racional::denominador() const {…} void Racional::escreve() const {…} … bool Racional::cumpreInvariante() const {…} … 22 Introdução à Programação 2003/2004 Métodos afectados void Racional::escreve() const { assert(cumpreInvariante()); cout << numerador(); if(denominador() != 1) cout << '/' << denominador(); assert(cumpreInvariante()); } 23 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. Introdução à Programação (Hmmm… Há excepções. ) 2003/2004 Métodos afectados void Racional::escreve() const { assert(cumpreInvariante()); cout << numerador(); if(denominador() != 1) cout << '/' << denominador(); } 24 Introdução à Programação 2003/2004 Atenção! Utilização sistemática de const tem grandes vantagens: 25 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) Introdução à Programação 2003/2004 Dica: Quando se constrói um racional à custa de outro, é invocado um Desafio construtor por cópia, fornecido automaticamente pelo compilador, e que se limita a copiar os atributos um a um. Quantas invocações de rotinas estão envolvidas em Racional r(1, 3); Racional s = r + 2; ignorando asserções? 26 Introdução à Programação 2003/2004 Construtor Racional::Racional() Racional::Racional(int const n) : numerador(n), denominador(1) { assert(cumpreInvariante()); assert(numerador() == n * denominador()); } 27 Introdução à Programação 2003/2004 Construtor Racional::Racional() Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 28 Introdução à Programação 2003/2004 Inspector Racional::numerador() int Racional::numerador() const { assert(cumpreInvariante()); return numerador_; } 29 Introdução à Programação 2003/2004 Inspector Racional::denominador() int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 30 Introdução à Programação 2003/2004 Operador Racional::operator+=() Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 31 Introdução à Programação 2003/2004 Método auxiliar 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); } 32 Introdução à Programação 2003/2004 Operador Racional::operator+() Racional const operator+(Racional r1, Racional const& r2) { r1 += r2; return r1; } 33 Introdução à Programação 2003/2004 Operador Racional::operator==() bool operator==(Racional const& r1, Racional const& r2) { return r1.numerador() == r2.numerador() and r1.denominador() == r2.denominador(); } 34 Introdução à Programação 2003/2004 Número de invocações: Traçado 0 Racional r(1, 3); Racional s = r + 2; 35 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 36 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 37 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 38 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 39 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 40 Introdução à Programação 2003/2004 Número de invocações: Traçado 1 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 41 Introdução à Programação 2003/2004 Número de invocações: Traçado 2 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 42 Introdução à Programação 2003/2004 Número de invocações: Traçado 2 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 43 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 int mdc(int const m, int const n) { … } 44 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 int mdc(int const m, int const n) { … } 45 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 46 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 47 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 48 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 49 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 Racional::Racional(int const n, int const d) { assert(d != 0); if(d < 0) { numerador_ = denominador_ } else { numerador_ = denominador_ } -n; = -d; n; = d; reduz(); assert(cumpreInvariante()); assert(numerador() * d == n * denominador()); } 50 Introdução à Programação 2003/2004 Número de invocações: Traçado 3 Racional r(1, 3); Racional s = r + 2; 51 Introdução à Programação 2003/2004 Número de invocações: Traçado 4 Racional::Racional(int const n) : numerador(n), denominador(1) { assert(cumpreInvariante()); assert(numerador() == n * denominador()); } 52 Introdução à Programação 2003/2004 Número de invocações: Traçado 4 Racional::Racional(int const n) : numerador(n), denominador(1) { assert(cumpreInvariante()); assert(numerador() == n * denominador()); } 53 Introdução à Programação 2003/2004 Número de invocações: Traçado 4 Racional r(1, 3); Racional s = r + 2; 54 Introdução à Programação 2003/2004 Número de invocações: Traçado 5 Racional const operator+(Racional r1, Racional const& r2) { r1 += r2; return r1; } 55 Passagem por valor implica cópia! Introdução à Programação 2003/2004 Número de invocações: Traçado 6 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } Construtor por cópia fornecido automaticamente pelo compilador. 56 Introdução à Programação 2003/2004 Número de invocações: Traçado 6 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } 57 Introdução à Programação 2003/2004 Número de invocações: Traçado 6 Racional const operator+(Racional r1, Racional const& r2) { r1 += r2; return r1; } 58 Introdução à Programação 2003/2004 Número de invocações: Traçado 7 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 59 Introdução à Programação 2003/2004 Número de invocações: Traçado 7 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 60 Introdução à Programação 2003/2004 Número de invocações: Traçado 8 int Racional::numerador() const { assert(cumpreInvariante()); return numerador_; } 61 Introdução à Programação 2003/2004 Número de invocações: Traçado 8 int Racional::numerador() const { assert(cumpreInvariante()); return numerador_; } 62 Introdução à Programação 2003/2004 Número de invocações: Traçado 8 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 63 Introdução à Programação 2003/2004 Número de invocações: Traçado 9 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 64 Introdução à Programação 2003/2004 Número de invocações: Traçado 9 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 65 Introdução à Programação 2003/2004 Número de invocações: Traçado 9 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 66 Introdução à Programação 2003/2004 Número de invocações: Traçado 10 int Racional::numerador() const { assert(cumpreInvariante()); return numerador_; } 67 Introdução à Programação 2003/2004 Número de invocações: Traçado 10 int Racional::numerador() const { assert(cumpreInvariante()); return numerador_; } 68 Introdução à Programação 2003/2004 Número de invocações: Traçado 10 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 69 Introdução à Programação 2003/2004 Número de invocações: Traçado 11 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 70 Introdução à Programação 2003/2004 Número de invocações: Traçado 11 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 71 Introdução à Programação 2003/2004 Número de invocações: Traçado 11 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 72 Introdução à Programação 2003/2004 Número de invocações: Traçado 11 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 73 Introdução à Programação 2003/2004 Número de invocações: Traçado 12 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 74 Introdução à Programação 2003/2004 Número de invocações: Traçado 12 int Racional::denominador() const { assert(cumpreInvariante()); return denominador_; } 75 Introdução à Programação 2003/2004 Número de invocações: Traçado 12 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 76 Introdução à Programação 2003/2004 Número de invocações: Traçado 12 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 77 Introdução à Programação 2003/2004 Número de invocações: Traçado 13 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 78 Introdução à Programação 2003/2004 Número de invocações: Traçado 13 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 79 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 int mdc(int const m, int const n) { … } 80 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 int mdc(int const m, int const n) { … } 81 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 82 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 83 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 84 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 void Racional::reduz() { assert(denominador_ != 0); int const divisor = mdc(numerador_, denominador_); numerador_ /= divisor; denominador_ /= divisor; assert(denominador_ != 0); assert(mdc(numerador_, denominador_) == 1); } 85 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 Racional& Racional::operator+=(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante()); numerador_ = numerador() * r2.denominador() + r2.numerador() * denominador(); denominador_ *= r2.denominador(); reduz(); assert(cumpreInvariante()); return *this; } 86 Introdução à Programação 2003/2004 Número de invocações: Traçado 14 Racional const operator+(Racional r1, Racional const& r2) { r1 += r2; return r1; Devolução por valor implica cópia! } 87 Introdução à Programação 2003/2004 Número de invocações: Traçado 15 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } 88 Introdução à Programação 2003/2004 Número de invocações: Traçado 15 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } 89 Introdução à Programação 2003/2004 Número de invocações: Traçado 15 Racional r(1, 3); Racional s = r + 2; 90 Introdução à Programação 2003/2004 Número de invocações: Traçado 16 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } 91 Introdução à Programação 2003/2004 Número de invocações: Traçado 16 Racional::Racional(Racional const& original) : numerador_(original.numerador_), denominador_(original.denominador_) { } 92 Introdução à Programação 2003/2004 Número de invocações: Traçado 16 Racional r(1, 3); Racional s = r + 2; // Fim! 93 Introdução à Programação 2003/2004 Conclusão Há muito mais invocações do que suspeitávamos: 16! Cada invocação implica: 94 Ok, ok… Uma das cópias provavelmente não seria feita… 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 Introdução à Programação 2003/2004 Eficiência Dados Conclusões 95 Programa passa 80% do tempo em 20% do código 20% críticos desconhecidos a priori Esforços de optimização antecipados são perda de tempo Introdução à Programação 2003/2004 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á… 96 Introdução à Programação 2003/2004 Rotinas em-linha (inline) Corpo não existe num único local, sendo executado sempre que desejado Corpo é substituído pelo compilador em todos ou três linhas, digamos, os locais onde a rotinaDuas é invocada excluindo asserções. 97 Rotinas curtas e sem ciclos devem ser emlinha! Introdução à Programação 2003/2004 Exemplo Geram mesmo código máquina! inline int soma(int const a, int const b) { return a + b; } int int int int x1 = 10; x2 = 30; x3 = 50; r = 0; int int int int int main() { r = x1 + x2; r = r + x3; } é globais int main() Usar variáveis { má ideia! Isto é r = soma(x1, x2); exemplo! r = soma(r, x3); } 98 x1 = 10; x2 = 30; x3 = 50; r = 0; só um Introdução à Programação 2003/2004 Sem inline (nem optimização). Exemplo em MAC-1 int soma(int const a, int const b) { return a + b; } int int int int x1 = 10; x2 = 30; x3 = 50; r = 0; int main() { r = soma(x1, x2); r = soma(r, x3); } 99 main: compilação soma: Introdução à Programação jump main # Variáveis: x1 = 10 x2 = 30 x3 = 50 r = 0 # Aqui faz-se a soma: 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. halt lodl 2 addl 1 retn 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 0 ac ? sp 100 … pilha Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 0 100 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 5 ac ? sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 1 101 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 6 ac 10 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 2 102 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 7 ac 10 sp 99 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? 10 ? 3 103 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 8 ac 30 sp 99 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? 10 ? 4 104 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 1 addl 2 retn pc 9 ac 30 sp 98 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? 30 10 ? 5 105 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 20 ac 30 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 6 106 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 21 ac 10 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 7 107 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 22 ac 40 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 8 108 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 10 ac 40 sp 98 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 9 109 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 0 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 11 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 10 110 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 12 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 11 111 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 13 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 10 ? 12 112 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 14 ac 40 sp 99 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 40 ? 13 113 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 15 ac 50 sp 99 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 30 40 ? 14 114 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 16 ac 50 sp 98 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 10 50 40 ? 15 115 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 20 ac 50 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 16 116 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 21 ac 40 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 17 117 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 22 ac 90 sp 97 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 18 118 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 17 ac 90 sp 98 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 19 119 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 40 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 18 ac 90 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 20 120 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 soma: 20 21 22 jump main 10 30 50 90 lodd x1 push lodd x2 push call soma insp 2 stod r lodd r push lodd x3 push call soma insp2 stod r halt lodl 2 addl 1 retn pc 19 ac 90 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? 17 50 40 ? 21 121 Introdução à Programação 2003/2004 Exemplo em MAC-1 Com inline. inline int soma(int const a, int const b) { return a + b; } int int int int x1 = 10; x2 = 30; x3 = 50; r = 0; main: compilação jump main # Variáveis: x1 = 10 x2 = 20 x3 = 30 r = 0 # Aqui faz-se a soma: 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 int main() { r = soma(x1, x2); r = soma(r, x3); } 122 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 0 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 0 ac ? sp 100 … pilha Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 0 123 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 0 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 5 ac ? sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 1 124 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 0 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 6 ac 10 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 2 125 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 0 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 7 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 3 126 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 40 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 8 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 4 127 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 40 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 9 ac 40 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 5 128 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 40 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 10 ac 90 sp 100 … Instruções executadas: 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? 6 129 Introdução à Programação 2003/2004 0 x1: 1 x2: 2 x3: 3 r: 4 main: 5 6 7 8 9 10 11 jump main 10 30 50 90 lodd x1 addd x2 stod r lodd r addd x3 stod r halt pc 11 ac 90 sp 100 … Instruções executadas: 7 130 93 94 95 96 97 98 99 100 ? ? ? ? ? ? ? ? Sem inline eram 21… Introdução à Programação 2003/2004 Comparação Sem inline: Sempre mais rápido! jump main # Variáveis: x1 = 10 x2 = 30 x3 = 50 r = 0 # Aqui faz-se a soma: 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. halt lodl 2 addl 1 retn Com inline: Sempre mais curto? main: main: Código demasiado longo pode tornar a execução mais lenta, pois obriga a soma: recorrer à memória virtual. 131 Introdução à Programação jump main # Variáveis: x1 = 10 x2 = 20 x3 = 30 r = 0 # Aqui faz-se a soma: 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 Não! Por vezes é mais longo. Depende da dimensão do código das rotinas, do número de invocações, etc. 2003/2004 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) … { … } 132 Introdução à Programação 2003/2004 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) {…} Construtores inline Racional::Racional(int const n, int const d) {…} inline int Racional::numerador() const {…} Inspectores inline int Racional::denominador() const {…} inline Racional::escreve() const {…} (continua) 133 Introdução à Programação 2003/2004 Estrutura global do programa (II) (continuação) void Racional::lê() {…} inline Racional& Racional::operator++() {…} inline Racional& Racional::operator--() {…} Modificadores 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() {…} Auxiliares inline bool Racional::cumpreInvariante() const {…} (continua) 134 Introdução à Programação 2003/2004 Estrutura global do programa (III) (continuação) /** Produto de dois racionais. @pre V. @post operator* = r1 × r2. */ inline Racional const operator*(Racional r1, Racional const& r2) {…} Operadores aritméticos não-membro /** 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) 135 Introdução à Programação 2003/2004 Estrutura global do programa (IV) (continuação) Operadores de igualdade e diferença /** 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() {…} 136 Introdução à Programação 2003/2004 Operadores em falta Aritméticos: Relacionais: <, <=, > e >= Incrementação: Unários: + e - Sufixo: ++ e -- Começamos aqui. Inserção e extracção de canais: << e >> Fica por fazer... 137 Introdução à Programação 2003/2004 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()); } 138 return *this; Introdução à Programação 2003/2004 Incrementação sufixa 139 Introdução à Programação 2003/2004 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 Aha!} Cá está a diferença! racional Trata-se o operador como se com o valor antes de incrementado, i.e., devolve-se fosse binário, recebendo como a cópia argumento um inteiro com valordo racional r. uma cópia de r antes de não especificado Faz-se e irrelevante. se incrementar, recorrendo à incrementação prefixa, já definida. 140 Introdução à Programação 2003/2004 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; } 141 Introdução à Programação 2003/2004 Operador - unário: operação /** … */ class Racional { public: … /** Devolve simétrico do racional. @pre V. @post operator- = -*this. */ Racional const operator-() const; … private: … }; 142 Introdução à Programação 2003/2004 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; } 143 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. Introdução à Programação 2003/2004 Operador + unário /** Devolve o racional. @pre V. @post operator+ ≡ r. */ inline Racional const& operator+(Racional const& r) { return r; } 144 Introdução à Programação 2003/2004 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 145 Introdução à Programação 2003/2004 Operadores <=, > e >= Podem-se definir à custa do operador < Como? 146 Introdução à Programação 2003/2004 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; } 147 Introdução à Programação 2003/2004 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); } 148 Introdução à Programação 2003/2004 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); } 149 Introdução à Programação 2003/2004 Outro desafio (fácil) 150 Definir o operador == à custa do operador < Introdução à Programação 2003/2004 Último problema Operador /= usa o operador !=, definido mais tarde Melhor solução: 151 concentrar declarações de rotinas não-membro após definição da classe Definir rotinas não-membro mais tarde Introdução à Programação 2003/2004 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 {…}; 152 Introdução à Programação 2003/2004 Declaração de rotinas não-membro (I) Incrementação e decrementação sufixas /** 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); 153 Introdução à Programação 2003/2004 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); Operadores aritméticos binários /** 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); 154 Introdução à Programação 2003/2004 Declaração de rotinas não-membro (III) Operador identidade Operadores de igualdade e diferença /** 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); 155 Introdução à Programação 2003/2004 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); Operadores relacionais /** 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); 156 Introdução à Programação 2003/2004 Definição de métodos (I) Construtores Inspectores Modificadores Auxiliares 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) 157 Introdução à Programação 2003/2004 Definição de rotinas não-membro Incrementação e decrementação sufixas inline Racional const operator++(Racional& r, int) {…} inline Racional const operator--(Racional& r, int) {…} inline Racional const operator*(Racional r1, Racional const& r2) {…} Operadores aritméticos binários 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) {…} Operador identidade Operadores de igualdade e diferença 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) {…} Operadores relacionais 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() {…} 158 Introdução à Programação 2003/2004 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) 159 Introdução à Programação 2003/2004 TAD Racional (inspectores) (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) 160 Introdução à Programação 2003/2004 TAD Racional (inspectores) (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) 161 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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) 162 Introdução à Programação 2003/2004 TAD Racional (modificadores) (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) 163 Introdução à Programação 2003/2004 TAD Racional (implementaçã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_; }; 164 Introdução à Programação 2003/2004 Aula 13: Sumário Definindo constantes de uma classe: Evitando o mecanismo de invocação de rotinas no código máquina produzido pelo compilador: 165 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. 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. Introdução à Programação 2003/2004