X operator +(const X&, const X&);
Oque aumenta ainda mais o polimorfismo do operador '+' o qual passa a ser usado também para a adição de dois objetos da classe X:
X x1, x2, x3;
.
.
.
x3=x1+x2;
x3=operator+(x1, x2);
css X
{.
.
.
friend X operator+(const X&, const X&);
.
.
.
};
class X
{
public:
X operator+(const X&) const;
.
.
.
};
x3=x1+x2;
x3=x1.operator+(x2);
No caso de polimorfismo envolvendo funções que não sejam operadores, possam ter funções ou métodos com o mesmo identificador mas que são distinguidos pelo número e/ou tipo dos parametros. Como exemplo sejam os métodos da classe A:
Sejam os métodos da classe A:
class A
{
int a;
public:
virtual void f();
void f(int);
void f(int, float);
void f(float, int);
};
Há quatro versões do método f na classe. No exemplo a seguir os métodos acoplados as respectivas mensagens são determinados em tempo de compilação em função do número e/ou tipo dos argumentos da mensagem. Quando o acoplamento mensagem/metódo é resolvido em tempo de compilação é chamado "acoplamento estático" ou "junção anterior".
A a;
a.f(); //A::f()
a.f(2); //A::f(int)
a.f(2, 3); //O compilador não sabe, não há um método f(int, int)
Na classe A tinhamos quatro versões de um método declarado, ou seja, uma instância de A pode responder a quatro tipos distintos de mensagens cujo seletor é f. A uma outra faceta de polimorfismo relacionada com o mecanismo de herança. Uma classe derivada, pode redefinir um método de uma classe base, com o mesmo identificador, como mesmo tipo de identificador, mesmo tipo de retorno e um corpo que pode ser diferente. Isto é feito na classe B a seguir, derivada da classe A:
class B: public A
{
int b, c;
public:
void f();
void f(int);
};
Note que uma instância de B pode responder a 6 mensagens cujo seletor é f, quatro para métodos herdados de A, e dois para métodos redefinidos em B.
Em C++ para mensagens enviadas a um objeto criado estáticamente, o acoplamento sempre será estático como no exemplo abaixo:
B b;
b.f(); //B::f()
b.f(2); //B::f(int)
b.A::f() //A::f() Nome qualificado
**** IMPORTANTÍSSIMO ****
As coisas mudam quando uma mensagem é enviada a um objeto através de um ponteiro. Em C++ e em Java um ponteiro para uma classe base pode receber um endereço de um objeto desta classe ou de qualquer classe que dela derivada direta ou indiretamente. Assim, se tivermos
A* pa;
.
.
.
pa->f(); //A::f()
podemos nos perguntar qual método será acoplado à mensagem, uma vez que o ponteiro pa pode receber o endereço de um objeto da classe A, da classe B ou de qualquer outra que derive de A. A resposta a essa questão depende do método A::f() ser virtual ou não:
- Se o método não for virtual, então A::f() será acoplado(independentemente do tipo do objeto cujo endereço é atribuido a pa), uma vez que pa é um ponteiro para a classe A, ou seja, o acoplamento é estático(determinado em tempo de compilação). [Se não for virtual vai ser estático, foda-se oque aconteça. Nota do professor]
- Se um método for virtual então o método a se acoplado a mensagem será aquele da classe do objeto cujo endereço, em tempo de execução, for atribuido a pa. Este tipo de acoplamento é chamado "acoplamento dinâmico" ou" junção posterior", pois é resolvido em tempo de execução.
Observações:
- Em uma classe derivada a redefinição de um método não virtual herdado de uma classe base esconde um método da classe base. A redefinição de um método virtual na classe derivada sobrecarrega um método da base.
- Em Java, todos os métodos não estáticos são virtuais.
- Acoplamento dinâmico tem um custo em termos de eficiência. Em C++ a junção posterior é implementada através de tabelas de ponteiros de métodos virtuais(TPMV). Toda classe que tiver declarado pelo menos um método virtual terá associada uma tabela cujas entradas serão os endereços do código de cada uma das funções virtuais declaradas na classe. Toda função virtual possui como atributo um índice para a entrada correspondente na TPMV de sua classe. A TPMV de uma classe derivada copia a TPMV de sua classe base, ajustando as entradas para apontar para aqueles métodos que foram sobrecarregados e foram adicionando novas entradas se novos métods virtuais forem declarados da classe derivada. Por exemplo a TPMV da classe A é:
0| &A::f() |
1 | &A::f(int, float) |
A TPMV da classe B é:
B
0 | &B::f() |
1 | &A::f(int, float) |
2 | &B::k() |


7:14 AM
Unknown
0 comentários:
Postar um comentário