aula 11 tipos abstractos de dados ii. 2003/2004 introdução à programação 2 estrutura global do...
TRANSCRIPT
Aula 11
Tipos Abstractos de Dados II
2003/2004Introdução à Programação2
Estrutura global do programa
#include <iostream>#include <cassert>
using namespace std;
int mdc(int const m, int const n) {…}
class Racional {…};
Racional::Racional(int const n) {…}
Racional::Racional(int const n, int const d) {…}
void Racional::escreve() {…}
Racional Racional::somaCom(Racional const& r2) {…}
void Racional::lê() {…}
void Racional::reduz() {…}
bool Racional::cumpreInvariante() {…}
int main() {…}
Diferente!Porquê?
2003/2004Introdução à Programação3
TAD Racional (I)
/** Representa números racionais. @invariant 0 < denominador mdc(numerador, denominador) = 1. */class Racional { public: /** Constrói racional com valor inteiro. @pre V. @post *this = n. */ Racional(int const n = 0);
/** Constrói racional correspondente a n/d. @pre d ≠ 0. @post *this = n/d. */ Racional(int const n, int const d);
/** 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)
2003/2004Introdução à Programação4
TAD Racional (II)
(continuação)
/** Devolve a soma de dois racionais. @pre V. @post somaDe = *this + r2. */ Racional somaCom(Racional const& r2);
/** 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ê();
(continua)
Diferente!
2003/2004Introdução à Programação5
TAD Racional (III)
(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ção6
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
2003/2004Introdução à Programação7
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
main()
2003/2004Introdução à Programação8
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …
main()
2003/2004Introdução à Programação9
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …
main()
r1: Racional
2003/2004Introdução à Programação10
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int {frozen}
0
*this: Racional&
2003/2004Introdução à Programação11
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int {frozen}
0
numerador: int
0
*this: Racional&
2003/2004Introdução à Programação12
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int {frozen}
0
numerador: int
0
denominador: int
1
*this: Racional&
2003/2004Introdução à Programação13
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int {frozen}
0
numerador: int
0
denominador: int
1
*this: Racional&
2003/2004Introdução à Programação14
Traçado
bool Racional::cumpreInvariante() { return 0 < denominador and
mdc(numerador, denominador) == 1;}
Introduza …
Racional::cumpreInvariante()
main()
r1: Racional
numerador: int
0
denominador: int
1
*this: Racional&
2003/2004Introdução à Programação15
Traçado
bool Racional::cumpreInvariante() { return 0 < denominador and
mdc(numerador, denominador) == 1;}
Introduza …
Racional::cumpreInvariante()
main()
r1: Racional
numerador: int
0
denominador: int
1
*this: Racional&
Devolve verdadeiro.
2003/2004Introdução à Programação16
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int
0
numerador: int
0
denominador: int
1
*this: Racional&
2003/2004Introdução à Programação17
Traçado
Racional::Racional(int const n) : numerador(n), denominador(1){
assert(cumpreInvariante()); }
Introduza …
Racional::Racional()
main()
r1: Racional
n: int
0
numerador: int
0
denominador: int
1
*this: Racional&
2003/2004Introdução à Programação18
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …
main()
r1: Racional
numerador: int
0
denominador: int
1
r2: Racional
numerador: int
0
denominador: int
1
2003/2004Introdução à Programação19
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
2003/2004Introdução à Programação20
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
2003/2004Introdução à Programação21
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
2003/2004Introdução à Programação22
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
Sem asserções, para encurtar.
*this: Racional&
2003/2004Introdução à Programação23
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
?
d: int
?
*this: Racional&
2003/2004Introdução à Programação24
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
?
d: int
?
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação25
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação26
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação27
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação28
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação29
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
2003/2004Introdução à Programação30
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
r1: Racional
numerador = 6denominador = 1
2003/2004Introdução à Programação31
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 6denominador = 9
2003/2004Introdução à Programação32
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 6denominador = 9
2003/2004Introdução à Programação33
Traçado
void Racional::reduz() { assert(denominador != 0);
int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor;
assert(denominador != 0); assert(mdc(numerador,
denominador) == 1);}
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::reduz()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 6denominador = 9
2003/2004Introdução à Programação34
Traçado
void Racional::reduz() { assert(denominador != 0);
int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor;
assert(denominador != 0); assert(mdc(numerador,
denominador) == 1);}
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::reduz()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 6denominador = 9
divisor: int {frozen}
3
Ignoram-se asserções, para encurtar.
2003/2004Introdução à Programação35
Traçado
void Racional::reduz() { assert(denominador != 0);
int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor;
assert(denominador != 0); assert(mdc(numerador,
denominador) == 1);}
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::reduz()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 6denominador = 9
divisor: int {frozen}
3
r1: Racional
numerador = 2denominador = 9
2003/2004Introdução à Programação36
Traçado
void Racional::reduz() { assert(denominador != 0);
int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor;
assert(denominador != 0); assert(mdc(numerador,
denominador) == 1);}
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::reduz()
*this: Racional&
r1: Racional
numerador = 2denominador = 9
divisor: int {frozen}
3
r1: Racional
numerador = 2denominador = 3
2003/2004Introdução à Programação37
Traçado
void Racional::reduz() { assert(denominador != 0);
int const divisor = mdc(numerador, denominador); numerador /= divisor; denominador /= divisor;
assert(denominador != 0); assert(mdc(numerador,
denominador) == 1);}
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::reduz()
*this: Racional&
divisor: int {frozen}
3
r1: Racional
numerador = 2denominador = 3
2003/2004Introdução à Programação38
Traçado
void Racional::lê() { int n, d;
cin >> n >> d; if(not cin.fail()) if(d == 0) cin.setstate(ios_base::failbit); else { if(d < 0) { numerador = -n; denominador = -d; } else { numerador = n; denominador = d; } reduz(); } }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 0denominador = 1
Racional::lê()
n: int
6
d: int
9
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
2003/2004Introdução à Programação39
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …6 9 7 3
main()
r1: Racional
numerador = 2denominador = 3
r2: Racional
numerador = 0denominador = 1
r2: Racional
numerador = 7denominador = 3
2003/2004Introdução à Programação40
Traçado
int main() { // Ler fracções: cout << "Introduza duas fracções (numer… Racional r1, r2; r1.lê(); r2.lê();
if(cin.fail()) { cout << "Opps! A leitura dos racion… return 1; }
(continua)
Introduza …6 9 7 3
main()
r1: Racional
numerador = 2denominador = 3
r2: Racional
numerador = 7denominador = 3
2003/2004Introdução à Programação41
Traçado
(continuação)
// Calcular racional soma: Racional r = r1.somaCom(r2);
// Escrever resultado: cout << "A soma de "; r1.escreve(); cout << " com "; r2.escreve(); cout << " é "; r.escreve(); cout << '.' << endl; }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 2denominador = 3
r2: Racional
numerador = 7denominador = 3
Medonho…
2003/2004Introdução à Programação42
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
2003/2004Introdução à Programação43
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
r: Racional
numerador = 0denominador = 1
2003/2004Introdução à Programação44
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
r: Racional
numerador = 0denominador = 1
r: Racional
numerador = 27denominador = 1
2003/2004Introdução à Programação45
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
r: Racional
numerador = 27denominador = 1
r: Racional
numerador = 27denominador = 9
2003/2004Introdução à Programação46
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
r: Racional
numerador = 27denominador = 9
Qual será a instância implícita?
r: Racional
numerador = 3denominador = 1
2003/2004Introdução à Programação47
Traçado
Racional Racional::somaCom(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
Introduza …6 9 7 3
main()
r2: Racional
numerador = 7denominador = 3
Racional::somaCom()
*this: Racional&
r1: Racional
numerador = 6denominador = 1
r1: Racional
numerador = 2denominador = 3
r2: Racional const&
r: Racional
numerador = 3denominador = 1
2003/2004Introdução à Programação48
Traçado
(continuação)
// Calcular racional soma: Racional r = r1.somaCom(r2);
// Escrever resultado: cout << "A soma de "; r1.escreve(); cout << " com "; r2.escreve(); cout << " é "; r.escreve(); cout << '.' << endl; }
Introduza …6 9 7 3
main()
r1: Racional
numerador = 2denominador = 3
r2: Racional
numerador = 7denominador = 3
r: Racional
numerador = 3denominador = 1
É igual ao racional devolvido por Racional::somaCom(), mas não é a mesma instância!
2003/2004Introdução à Programação49
Traçado
Etc…..
2003/2004Introdução à Programação50
O nosso objectivo
#include <iostream>
using namespace std;
…
int main() { cout << "Introduza duas fracções (numerador denominador): "; Racional r1, r2; cin >> r1 >> r2; if(cin.fail()) { cout << "Opps! A leitura dos racionais falhou!" << endl; return 1; } Racional r = r1 + r2; cout << "A soma de " << r1 << " com " << r2 << " é " << r << '.' << endl; }
Hoje ficaremos mais próximos…
2003/2004Introdução à Programação51
Sobrecarga de operadores
Sobrecarga de rotinas possível: int soma() int soma(int const a) int soma(int const a, int const b)
Sobrecarga de operadores também!
2003/2004Introdução à Programação52
Operação Racional::operator+()/** … */class Racional { public: …
/** Devolve a soma de dois racionais. @pre V. @post somaDe = *this + r2. */ Racional operator+(Racional const& r2);
…
private: …
};
2003/2004Introdução à Programação53
Método Racional::operator+()Racional Racional::operator+(Racional const& r2) { assert(cumpreInvariante()); assert(r2.cumpreInvariante());
Racional r; r.numerador = numerador * r2.denominador + r2.numerador * denominador; r.denominador = denominador * r2.denominador;
r.reduz();
assert(cumpreInvariante()); assert(r.cumpreInvariante());
return r; }
2003/2004Introdução à Programação54
Função main()
int main(){ …
// Calcular racional soma:
Racional r = r1.operator+(r2);
… }
2003/2004Introdução à Programação55
Função main()
int main(){ …
// Calcular racional soma:
Racional r = r1 + r2;
… }
2003/2004Introdução à Programação56
Sobrecarga de operadores
Como membro de classe: Primeiro operando é instância implícita! Outros operandos são passados como argumentos
Racional Racional::operator+(Racional const& r2)
Como rotina “livre”: Todos os operandos passados como argumentos
Racional operator+(Racional const& r1, Racional const& r2)
2003/2004Introdução à Programação57
Operadores a sobrecarregar
Aritméticos: Binários: *, /, + e - Unários: + e -
Relacionais: <, <=, > e >=
Igualdade e diferença: == e !=
Incrementação: Prefixo: ++ e -- Sufixo: ++ e --
Especiais de atribuição: *=, /=, += e -=
Vamos começar por aqui.
2003/2004Introdução à Programação58
Um problema
Que devolve o operador ++ prefixo?
Exemplo:
int i = 0;++(++i);cout << i << endl;
2
Tem de resultar no próprio i, e não numa cópia.
É um exemplo do que é possível fazer, e não uma sugestão de que é recomendável!
2003/2004Introdução à Programação59
Um caso bicudo
int main(){ int i = 0; ++(++i); cout << i << endl;}
2003/2004Introdução à Programação60
E um regresso às origens
void incrementa(int& v){ v = v + 1;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
Invocação impossível!incrementa() não devolve nada.
2003/2004Introdução à Programação61
Correcção…
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
2003/2004Introdução à Programação62
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
2003/2004Introdução à Programação63
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
2003/2004Introdução à Programação64
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
2003/2004Introdução à Programação65
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
2003/2004Introdução à Programação66
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
incrementa()
v: int&
2003/2004Introdução à Programação67
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
incrementa()
v: int&
i: int
1
2003/2004Introdução à Programação68
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
incrementa()
v: int&
i: int
1
: int
1
Valor devolvido é cópia de v, i.e., é cópia de i. É instância temporária!
2003/2004Introdução à Programação69
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
: int
1
2003/2004Introdução à Programação70
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
: int
1
incrementa()
v: int&
2003/2004Introdução à Programação71
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
: int
1
incrementa()
v: int&
: int
2
2003/2004Introdução à Programação72
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
incrementa()
v: int&
: int
2
: int
2
Valor devolvido é cópia de v, i.e., é cópia da cópia alterada de i.
2003/2004Introdução à Programação73
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
: int
2
: int
2
2003/2004Introdução à Programação74
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
1
2003/2004Introdução à Programação75
Funcionará?
int incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
1
2003/2004Introdução à Programação76
Moral da história
Segunda incrementação falhou
Ou melhor, incrementou cópia
Porque devolução faz-se por valor
Necessário devolver por referência
2003/2004Introdução à Programação77
Em abono da verdade…
… o código nem sequer compila!
C++ proíbe referência (não constante) para instância temporária!
2003/2004Introdução à Programação78
Outra correcção…
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
2003/2004Introdução à Programação79
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
2003/2004Introdução à Programação80
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
2003/2004Introdução à Programação81
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
2003/2004Introdução à Programação82
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
2003/2004Introdução à Programação83
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
incrementa()
v: int&
2003/2004Introdução à Programação84
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
0
incrementa()
v: int&
i: int
1
2003/2004Introdução à Programação85
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
incrementa()
v: int&
i: int
1
: int&
2003/2004Introdução à Programação86
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
: int&
2003/2004Introdução à Programação87
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
incrementa()
v: int&
: int&
2003/2004Introdução à Programação88
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
1
incrementa()
v: int&
: int&
i: int
2
2003/2004Introdução à Programação89
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
2
incrementa()
v: int&
: int&
: int&
2003/2004Introdução à Programação90
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
2
: int&
: int&
2003/2004Introdução à Programação91
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
2
2
2003/2004Introdução à Programação92
E agora?
int& incrementa(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; incrementa(incrementa(i)); cout << i << endl;}
main()
i: int
2
2
2003/2004Introdução à Programação93
Finalmente… sobrecarga!
int& operator++(int& v){ v = v + 1;
return v;}
int main(){ int i = 0; ++(++i); cout << i << endl;}
Era doce… Não se pode redefinir os operadores dos tipos básicos!
Mas para um TAD pode-se sobrecarregar!
2003/2004Introdução à Programação94
Operação Racional::operator++()/** … */class Racional { public: …
/** Incrementa o racional.
@pre *this = r.
@post operator++ = ? *this = r + 1. */ Racional& operator++();
…
private: …
};
2003/2004Introdução à Programação95
Método Racional::operator++()Racional& Racional::operator++() { assert(cumpreInvariante());
numerador += denominador;
assert(cumpreInvariante());
return ?; }
Porque não é necessário reduzir a fracção?
Os TAD devem ter comportamente semelhante aos tipos básicos.Por isso, devia-se devolver o racional incrementado. Como?
2003/2004Introdução à Programação96
Método Racional::operator++()Racional& Racional::operator++() { assert(cumpreInvariante());
numerador += denominador;
assert(cumpreInvariante());
return *this; }
2003/2004Introdução à Programação97
Operação Racional::operator++()/** … */class Racional { public: …
/** Incrementa o racional.
@pre *this = r.
@post operator++ ≡ *this *this = r + 1. */ Racional& operator++();
…
private: …
};
O que se devolve é a instância implícita ela própria, e não uma instância temporária com igual valor.
2003/2004Introdução à Programação98
Igualdade vs. identidade
Duas instâncias
São idênticas se forem a mesma instância São iguais se tiverem o mesmo valor Se são idênticas, são também iguais. Podem ser iguais, mas não ser idênticas
2003/2004Introdução à Programação99
Igualdade vs. identidade
int cópia(int const& v){ return v;}
int& mesmo(int& v){ return v;}
int main(){ int i = 0;
mesmo(i) = 10;
cópia(i) = 20;}
Ok.
Erro!
2003/2004Introdução à Programação100
lvalue
Lvalue é uma entidade à qual se pode atribuir um valor
Instâncias temporárias não são lvalues, são rvalues
2003/2004Introdução à Programação101
Aula 11: Sumário
Revisões sobre a aula anterior *this como referência Construção dos atributos: lista de
inicializadores Devolução por valor e por referência Noções de identidade e igualdade Diferença entre lvalue e rvalue Introdução à sobrecarga de operadores para
TAD: operadores + binário e ++ prefixo