Aula 12
Tipos Abstractos de Dados III
Operadores a sobrecarregar





Feito.
Prefixo: ++ e -Sufixo: ++ e --
Especiais de atribuição:

2
== e !=
Incrementação:


<, <=, > e >=
Igualdade e diferença:


Binários: *, /, + e Unários: + e -
Relacionais:


Feito, mas precisa
ser refeito.
Aritméticos:
*=, /=, += e -=
Introdução à Programação
2003/2004
Por onde começar?


Devemos começar pelos operadores com
efeitos laterais
Quais são?
Racional a(1,2), b(3,5);
a + b; // altera a? e b?
a++;
// altera a?
3
Introdução à Programação
2003/2004
Operadores com efeitos laterais




Prefixo: ++ e -Sufixo: ++ e --
Especiais de atribuição:


Feito.
Incrementação:
*=, /=, += e -=
De atribuição:

=
Fica para POO.
(Aliás, aqui é
desnecessário.)
4
Introdução à Programação
2003/2004
Operação
Racional::operator++()
/** … */
class Racional {
public:
…
/** Incrementa o racional.
@pre *this = r.
@post operator++ ≡ *this  *this = r + 1. */
Racional& operator++();
…
private:
…
};
5
Introdução à Programação
2003/2004
Operação
Racional::operator--()
/** … */
class Racional {
public:
…
/** Decrementa o racional.
@pre *this = r.
@post operator-- ≡ *this  *this = r - 1. */
Racional& operator--();
…
private:
…
};
6
Introdução à Programação
2003/2004
Método
Racional::operator++()
Racional& Racional::operator++()
{
assert(cumpreInvariante());
numerador += denominador;
assert(cumpreInvariante());
return *this;
}
7
Introdução à Programação
2003/2004
Método
Racional::operator--()
Racional& Racional::operator--()
{
assert(cumpreInvariante());
numerador -= denominador;
assert(cumpreInvariante());
return *this;
}
8
Introdução à Programação
2003/2004
Operadores especiais de
atribuição

*=, /=, += e -=

Como membros da classe (porquê?)
Se é possível com tipos básicos,
também o deve ser com os
instância nossos
implícita
TAD.

Primeiro operando é

Isso não significa que este
Devolvem primeiro operando por
referência:
código seja recomendável!
Racional a(3), b(1, 2);
(a *= b) *= b;
9
Introdução à Programação
2003/2004
Operação
Racional::operator*=()
/** … */
class Racional {
public:
…
/** Multiplica por um racional.
@pre *this = r.
@post operator*= ≡ *this  *this = r × r2. */
Racional& operator*=(Racional const& r2);
…
private:
…
};
10
Introdução à Programação
2003/2004
Método
Racional::operator*=()
Racional& Racional::operator*=(Racional const& r2)
{
assert(cumpreInvariante());
assert(r2.cumpreInvariante());
numerador *= r2.numerador;
denominador *= r2.denominador;
reduz();
assert(cumpreInvariante());
return *this;
}
11
Introdução à Programação
2003/2004
Operação
Racional::operator/=()
/** … */
class Racional {
public:
…
/** Divide por um racional.
@pre *this = r  r2 ≠ 0.
@post operator/= ≡ *this  *this = r / r2. */
Racional& operator/=(Racional const& r2);
…
private:
…
};
12
Introdução à Programação
2003/2004
Método
Racional::operator/=()
Racional& Racional::operator/=(Racional const& r2)
{
assert(cumpreInvariante());
assert(r2.cumpreInvariante());
assert(r2 != 0);
numerador *= r2.denominador;
denominador *= r2.numerador;
Não se pode dividir por zero.
reduz();
assert(cumpreInvariante());
Asserção possível apenas
quando se definir operador !=.
return *this;
}
13
Introdução à Programação
2003/2004
Operação
Racional::operator+=()
/** … */
class Racional {
public:
…
/** Adiciona de um racional.
@pre *this = r.
@post operator+= ≡ *this  *this = r + r2. */
Racional& operator+=(Racional const& r2);
…
private:
…
};
14
Introdução à Programação
2003/2004
Método
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());
Pode-se trocar a ordem destas
atribuições?
return *this;
}
15
Introdução à Programação
2003/2004
Operação
Racional::operator-=()
/** … */
class Racional {
public:
…
/** Subtrai de um racional.
@pre *this = r.
@post operator-= ≡ *this  *this = r - r2. */
Racional& operator-=(Racional const& r2);
…
private:
…
};
16
Introdução à Programação
2003/2004
Método
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;
}
17
Introdução à Programação
2003/2004
Operadores aritméticos binários

*, /, + e –

Podem-se definir à custa dos operadores
especiais de atribuição!

Aliás, devem-se!

Como?
18
Introdução à Programação
2003/2004
Função operator*()
/** Produto de dois racionais.
@pre V.
@post operator* = r1 × r2. */
Racional const operator*(Racional r1, Racional const& r2)
{
r1 *= r2;
return r1;
}
Passagem por
Devolução
como
valor!
constante??
Alteração
de r1 é alteração de cópia do
Torna impossível
argumento.
Racional r1(1, 2), r2(3, 2);
++(r1 * r2);
tal como acontece para os tipos
básicos.
19
Introdução à Programação
2003/2004
Função operator/()
/** Quociente de dois racionais.
@pre r2 ≠ 0.
@post operator/ = r1 / r2. */
Racional const operator/(Racional r1, Racional const& r2)
{
assert(r2 != 0);
r1 /= r2;
return r1;
}
20
Introdução à Programação
2003/2004
Função operator+()
/** Soma de dois racionais.
@pre V.
@post operator+ = r1 + r2. */
Racional const operator+(Racional r1, Racional const& r2)
{
r1 += r2;
return r1;
}
21
Introdução à Programação
2003/2004
Função operator-()
/** Subtracção de dois racionais.
@pre V.
@post operator- = r1 - r2. */
Racional const operator-(Racional r1, Racional const& r2)
{
r1 -= r2;
return r1;
}
22
Operadores ou rotinas nãomembro, são externas à classe
C++, mas fazem parte da
definição do TAD!
Introdução à Programação
2003/2004
Discussão

Quase regra:


Métodos devem recorrer a operações
existentes



23
Se rotina pode não ser membro, não o deve ser
*= à custa de *
ou
* à custa de *=?
Introdução à Programação
Obriga a cópia
desnecessária
2003/2004
Conversões implícitas



Definidas por construtores invocáveis com
apenas um argumento
Convertem entre o tipo do parâmetro desse
construtor e a classe
Permitem:
Racional r(1, 3);
Racional s = r + 3;
24
Introdução à Programação
2003/2004
Conversões implícitas



Definidas por construtores invocáveis com
apenas um argumento
Convertem entre o tipo do parâmetro desse
construtor e a classe
Conversão
Permitem:
implícita,
Racional r(1, 3);
colocada pelo
compilador.
Racional s = r + Racional(3);
25
Introdução à Programação
2003/2004
Membro ou não membro, eis a
questão

Conversões implícitas

não ocorrem para instâncias através das quais se
invocam operações
(que são implícitas na execução do respectivo método)

Se operator+() fosse membro de Racional:
Racional r(1, 3);
Racional s = 3 + r;
26
Introdução à Programação
Erro!
Conversão
impossível!
2003/2004
Conversões explícitas
class Racional {
public:
…
explicit Racional(int const valor = 0);
…
};
…
Racional
Racional
Racional
Racional
Racional
27
Neste caso conversões
implícitas são desejáveis.
Não se deve usar
explicit.
r(1, 3);
s = 4;
t(4);
u = r + 3;
v = r + Racional(3);
Introdução à Programação
2003/2004
Operadores de igualdade e
diferença

Membros ou não membros?

Precisam de aceder aos atributos: membros

Convinha que tivessem comportamento comutativo no
que diz respeito às conversões: não membros
Racional r(1, 2);
if(r == 1) …
if(1 == r) …
28
Introdução à Programação
2003/2004
Inspectores

Operações que permitem obter informações
acerca de uma instância de um TAD

Muitas vezes permitem saber o valor de
atributos

Revelam informação sem violar
encapsulamento
29
Introdução à Programação
2003/2004
Dois inspectores úteis
class Racional {
public:
…
/** 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();
…
private:
…
};
30
int numerador_;
int denominador_;
Não pode haver operações
com mesmo nome de
atributos.
Optou-se por alterar nome
de atributos. Porquê?
Introdução à Programação
2003/2004
Dois inspectores úteis
int Racional::numerador()
{
assert(cumpreInvariante());
assert(cumpreInvariante());
return numerador_;
}
int Racional::denominador()
{
assert(cumpreInvariante());
assert(cumpreInvariante());
return denominador_;
}
31
Introdução à Programação
2003/2004
Predicado operator==()
/** Indica se dois racionais são iguais.
@pre V.
Sempre? De certeza?
@post operator== = (r1 = r2). */
bool operator==(Racional const& r1, Racional const& r2)
{
As instruções de asserção
return r1.numerador() == r2.numerador()não
ando garantem?
r1.denominador() == r2.denominador();
}
Sim, mas…

Funciona porque os inspectores se limitam a devolver os atributos e
estes cumprem sempre a CIC, ou seja,
0 < denominador_  mdc(numerador_, denominador_) = 1
32
Introdução à Programação
2003/2004
Bronca…
Racional& Racional::operator/=(Racional const& r2)
{
assert(cumpreInvariante());
assert(r2.cumpreInvariante());
assert(r2 != 0);
numerador_ *= r2.denominador();
denominador_ *= r2.numerador();
reduz();
assert(cumpreInvariante());
}
33
return *this;
Introdução à Programação
Pois é… O numerador de
r2 pode ser negativo!
2003/2004
Correcção e… nova bronca!
Racional& Racional::operator/=(Racional const& r2)
{
assert(cumpreInvariante());
assert(r2.cumpreInvariante());
assert(r2 != 0);
if(r2.numerador() < 0) {
numerador_ *= -r2.denominador();
denominador_ *= -r2.numerador();
} else {
numerador_ *= r2.denominador();
denominador_ *= r2.numerador();
}
reduz();
assert(cumpreInvariante());
}
return *this;
E se r2 for o mesmo que
*this? Ou seja, se:
Racional r(1, 2);
r /= r;
34
Introdução à Programação
2003/2004
Correcção e… nova bronca!
Racional& Racional::operator/=(Racional const& r2)
{
assert(cumpreInvariante());
assert(r2.cumpreInvariante());
assert(r2 != 0);
int numerador = r2.numerador();
if(numerador < 0) {
numerador_ *= -r2.denominador();
denominador_ *= -numerador;
} else {
numerador_ *= r2.denominador();
denominador_ *= numerador;
}
reduz();
assert(cumpreInvariante());
}
35
return *this;
Introdução à Programação
2003/2004
E o operador !=?
/** Indica se dois racionais são diferentes.
@pre V.
@post operator!= = (r1 ≠ r2). */
bool operator!=(Racional const& r1, Racional const& r2)
{
return not (r1 == r2);
}
36
Introdução à Programação
2003/2004
Em falta

Aritméticos:


Relacionais:


<, <=, > e >=
Incrementação:

37
Unários: + e -
Sufixo: ++ e --
Introdução à Programação
2003/2004
Coisas várias

Uma trabalheira destas vale a pena?

O código está errado!
38
Introdução à Programação
2003/2004
Aula 12: Sumário




Sobrecarga de operadores para o TAD Racional
Operadores com efeitos laterais
Regra: uma rotina só deve ser membro se precisar de o ser
Conversões implícitas:








39
Construtores invocáveis com um único argumento
Excepções
Evitando as conversões implícitas com explicit
Comutatividade quanto a conversões
Devolução de constantes
Implementação de operações à custa de outras
Inspectores e sua utilidade
Vantagens e desvantagens de definir um TAD completo:
trabalho do produtor vs. trabalho do consumidor
Introdução à Programação
2003/2004
Download

Aula teórica - iscte-iul