O que é importante:
1. Perceber o que é redefinição de operadores.
2. Perceber a redefinição de operadores binários e unários
(postfix e prefix)
3. Perceber a utilização de grandes objectos.
4. Perceber as respectivas regras da linguagem C++.
5. Estar apto a construir programas que utilizem
as respectivas construções de POO.
Redefinição de operadores (Operator Overloading)
A linguagem de programação C++ permite ao programador redefinir
a maioria dos operadores, por forma a facilitar o seu uso no contexto
em que estão inseridos. O compilador vai gerar o código apropriado,
baseado na maneira como o operador é usado.
Os programadores podem usar tipos já existentes no C++ mas
também podem definir novos tipos. Os tipos já existentes no C++
podem ser usados com a vasta colecção de operadores desta
linguagem. Embora o C++ não permita a criação de novos
operadores, permite que os operadores existentes sejam redefinidos,
para que quando estes forem usados com objectos de classes
definidas pelo utilizador, os operadores tenham significado
apropriado a esse novo tipo.
class CData
{
unsigned m_Ano;
short int m_Mes;
short int m_Dia;
public:
CData();
CData(short int d, short int m, unsigned a);
~CData();
};
int main()
{
CData hoje;
CData ano_novo = CData(1, 1, 2004);
return 0;
}
CData::CData()
{
time_t t = time(0);
struct tm *today = localtime(&t);
m_Ano = today->tm_year + 1900;
m_Mes = today->tm_mon + 1;
m_Dia = today->tm_mday;
}
CData::~CData()
{
}
CData::CData(short int d, short int m, unsigned a)
{
m_Ano = a;
m_Mes = m;
m_Dia = d;
}
class CData
{
unsigned m_Ano;
short int m_Mes;
short int m_Dia;
public:
CData();
CData(short int d, short int m, unsigned a);
~CData();
int Diferenca (const CData& d) const;
};
int main()
{
CData hoje;
CData ano_novo = CData(1, 1, 2004);
int quantos_dias_faltam = ano_novo.Diferenca(hoje);
return 0;
}
class CData
{
unsigned m_Ano;
short int m_Mes;
short int m_Dia;
public:
CData();
CData(short int d, short int m, unsigned a);
~CData();
friend int gl_diferenca (const CData& d1, const CData& d2);
};
int main()
{
CData hoje;
CData ano_novo = CData(1, 1, 2004);
int quantos_dias_faltam = gl_diferenca (ano_novo, hoje);
return 0;
}
int quantos_dias_faltam = ano_novo.Diferenca(hoje);
class CData
{
unsigned m_Ano;
short int m_Mes;
short int m_Dia;
public:
CData();
CData(short int d, short int m, unsigned a);
~CData();
int operator - (const CData& d) const;
};
int main()
{
CData hoje;
CData ano_novo = CData(1, 1, 2004);
int quantos_dias_faltam = ano_novo - hoje;
return 0;
}
int quantos_dias_faltam = gl_diferenca (ano_novo, hoje);
A redefinição de operadores representa uma maneira alternativa
de chamar funções.
Em vez de especificar os argumentos entre os parêntesis, os
argumentos podem ser “interligados” com a ajuda de operadores.
Para os tipos built-in é a própria linguagem que faz a redefinição:
int a1 = 2, b1 = 3;
int c1 = a1 + a1 * b1;
double a2 = 2.2, b2 = 3.3;
double c2 = a2 + a2 * b2;
class CConjunto
{
public:
CConjunto operator+(const CConjunto& add) const;
CConjunto operator*(const CConjunto& inter) const;
//…
};
As regras de precedência mantêm-se!
CConjunto a3, b3;
//...
CConjunto c3 = a3 + a3 * b3;
A redefinição dos operadores faz-se escrevendo uma função (com
cabeçalho e corpo) como normalmente se faz para todas as funções,
com a única diferença de o nome da função contém sempre a
palavra-chave operator seguida do símbolo do operador que se quer
redefinir.
a palavra chave do C++
O nome de operador, por exemplo
+, -, /, *, ==, !=, ++, --, <, >, <<, etc.
o_valor_retorno operator#(lista_dos_argumentos)
{
// corpo da função operador
return o_valor_retorno;
}
EXEMPLO:
Se pretendermos redefinir o operador + de forma a efectuar
a soma de dois objectos da classe X, a declaração da função
poderá ser a seguinte:
class X
//definição da classe
{
int a;
public:
X(int aa) {a=aa;}
// redefinição do operador +
X operator+(const X&)const;
};
//corpo da função operador+
X X::operator+(const X& xr) const {
X tmp(0);
tmp.a = a+xr.a;
return X (a + xr.a);
return tmp;
}
Esta função operadora pode ser
chamada de duas maneiras diferentes:
X x1(0), x2(2), x3(3);
x1 = x2+x3 ;
//chamada implícita
x1 = x2.operator+(x3);//chamada explícita
x1 = x2 + x3 + x3;
x1 = x3.operator+ (x2.operator+ (x3));
Operadores unários e os de atribuição são executados
começando do lado direito.
Todos os operadores restantes são executados começando do
lado esquerdo.
X operator+(const X&)const; o operador
lista_dos_argumentos
o_valor_retorno
o nome da função
X X::operator+(const X& xr) const
{
return X(a + xr.a);
}
void main (void)
{
X x1=4,x2=5;
. . . . . . . . . . .
x1=x1+x2;
//chamada implícita
x1=x1.operator+(x2); //chamada explícita
. . . . . . . . . . .
}
Nem todos os operadores podem ser redefinidos. Os operadores de
C++ que podem ser redefinidos são seguintes:
+
|
-=
<<
>=
->
~
*=
>>
&&
[]
*
!
/=
>>=
||
()
/
=
%=
<<=
++
new
%
^
&
<
>
+=
^=
&=
|=
==
!=
<=
-->* ,
new[] delete
delete[]
Os operadores do C++ que não podem ser redefinidos são:
.*
::
.
?:
sizeof
typeid
Para além destas, existem outras restrições impostas à redefinição
de operadores, por parte da linguagem C++ :
1. A precedência dos operadores não pode ser modificada pela
redefinição. No entanto, o uso de parêntesis permite ultrapassar
esta restrição.
2. Não é possível modificar o número de operandos de um
operador. Os operadores unários que sejam redefinidos,
permanecem operadores unários; os operadores binários que
sejam redefinidos, permanecem binários; e ao único operador
ternário do C++ ( ?: ) não é permitida redefinição. Os operadores
& , * , + , - têm versões unárias e binárias,
podendo cada uma destas versões ser redefinida separadamente.
3. Não é permitido criar novos operadores. Só os operadores
existentes em C++ podem ser redefinidos, pelo que a lista completa
dos operadores que se podem redefinir encontra-se na primeira
tabela.
1. A precedência dos operadores não pode ser modificada pela
redefinição. No entanto, o uso de parêntesis permite ultrapassar
esta questão.
A = B + C*D;
3
2
1
A = (B + C)*D;
3
1
2
2. Não é possível modificar o número de operandos de um
operador.
A = B ++C;
3. Não é permitido criar operadores novos .
A= B  C;
EXEMPLO:
Supondo que poderíamos criar operadores novos,
criou-se o operador <- . Ao escrevermos a
seguinte expressão:
a<--b
o compilador não saberia se deveria
interpretar como:
a<-(-b) , ou como:
a<(--b).
Esta é uma das razões para não ser possível
criar operadores novos.
4. A redefinição de operadores apenas se aplica a objectos de tipos
definidos pelo utilizador, ou à mistura de um objecto pertencente a
um tipo definido pelo utilizador com um outro objecto pertencente
a um tipo já existente no C++. Pelo menos um operando da função
operadora tem de ser um objecto de uma classe, ou uma referência
a um objecto de uma classe. Nem sequer é possível definir uma
função operadora, que opere exclusivamente com ponteiros.
EXEMPLO:
O programador não pode redefinir o operador
+ para efectuar a soma de dois inteiros,
mas pode redefinir o operador + para
efectuar a soma de dois objectos de uma
classe X, definida pelo utilizador.
5. A redefinição de um operador não implica a redefinição
automática de operadores relacionados com o primeiro. Ou seja,
os operadores só podem ser redefinidos explicitamente e nunca
implicitamente.
EXEMPLO:
Sendo X, uma classe com os operadores + e =
redefinidos e sendo x1 e x2 dois objectos
dessa classe, é possível escrever a seguinte
instrução:
x1 = x1 + x2;
// Certo
No entanto, isto não implica que o operador
+= se possa utilizar:
x1 += x2;
// Errado
Para que esta instrução esteja correcta, tem que se redefinir
também o operador += .
Um operador binário pode ser redefinido como uma funçãomembro não estática que recebe um argumento ou como uma
função não-membro que recebe dois argumentos.
class X
{
int a;
public:
X(int aa) {a=aa;}
OU
X operator+(const X&)const;
friend X operator+(const X& xl, const X& xr);
};
X X::operator+(const X& xr) const
{
return X(a + xr.a);
}
X operator+ (const X& xl, const X& xr)
{
return X(xl.a + xr.a);
}
OU
Um operador unário pode ser redefinido como uma funçãomembro não estática que não tem argumentos ou como uma
função não-membro que recebe um argumento.
class X
{
int a;
public:
X(int aa) {a=aa;}
OU
const X operator-() const;
friend X operator-(const X& xr);
};
const X X::operator- () const
{
return (-a);
}
X operator-(const X& xr)
{
return X(-xr.a);
}
OU
int main()
{
X x1(0), x2(2), x3(3);
x1 = x2 + x3;
x1 = x2.operator+ (x3);
x1 = operator+ (x2, x3);
x1 = -x2;
x1 = x2.operator- ();
x1 = operator- (x2);
return 0;
}
função-membro
OU
função não
membro
OU
Implicações de os operadores serem membros da classe
ou funções amigas (friend functions)
As funções operadoras podem ser funções membro da classe ou
funções não membro da classe, sendo estas últimas, normalmente,
definidas como amigas. As funções membro da classe usam o
ponteiro this, de maneira a obter implicitamente um objecto da
classe que é argumento do operador. Pelo contrário, no caso de
uma função não membro da classe , todos os argumentos devem
ser explicitamente listados.
EXEMPLO:
Para o caso de operações binárias, uma classe
X pode ser declarada da seguinte forma:
class X {
//..........................
public:
X operator+(X);
//função membro, o 1º argumento é
//implicitamente passado através do this
friend X operator-(X,X);
//função global, não tem this.
X operator/(X,X);
//ERRO! Operação ternária.
// ..........................
};
class X {
//..........................
public:
X operator+(X);
..........................
X x1(...) ,x2(...), x3(...);
x1 = x2 + x3; // ou x1 = x2.operator+(x3);
função membro é o 1º argumento
o 2º argumento
O 1º argumento é implicitamente passado
através do this
class X {
//..........................
public:
friend X operator+(X,X);
..........................
X x1(...) ,x2(...), x3(...);
x1 = x2 + x3; // ou x1 = operator+(x2, x3);
o 1º argumento
o 2º argumento
O 1º argumento não pode ser passado
através do this
Download

ppt1