Upcasting
Quando o endereço de um objecto derivido (i.e. um ponteiro ou uma
referência) é tratado como se fosse o endereço do tipo base, este processo
chama-se upcasting.
Exemplo:
pessoa
aluno a;
empregado e;
pessoa* pa = &a;
pessoa& re = e;
upcasting
aluno
empregado
class pessoa
{
public:
void anunciar ()
{
cout << "Sou pessoa!" << endl;
}
};
class aluno : public pessoa
{
public:
void anunciar ()
{
cout << "Sou aluno!" << endl;
}
};
Sou pessoa!
?
void anuncio (pessoa& p)
{
p.anunciar();
}
int main (int argc, char* argv[])
{
aluno a1;
anuncio (a1);
}
return 0;
binding
early
late
Para causar a ligação dinâmica duma função particular usa-se a
palavra-chave virtual.
A ligação dinâmica só ocorre para as funções virtuais e desde que
seja utilizado o endereço daquela classe base na qual existem estas
funções virtuais.
Para criar uma função-membro como virtual, a declaração desta
função deve ser precedida pela palavra virtual.
pessoa.h
class pessoa
{
public:
virtual void anunciar ();
};
pessoa.cpp
void pessoa::anunciar()
{
cout << "Sou pessoa!" << endl;
};
Se uma função é definida como virtual numa classe base, esta
função é também virtual em todas as classes derivadas.
pessoa.h
class pessoa
{
public:
virtual void anunciar ();
aluno.h
class aluno : public pessoa
{
public:
void anunciar ();
};
};
pessoa.cpp
void pessoa::anunciar()
{
cout << "Sou pessoa!" << endl;
};
aluno.cpp
void aluno::anunciar()
{
cout << "Sou aluno!" << endl;
};
int main (int argc, char* argv[])
{
aluno a1;
anuncio (a1);
}
return 0;
Sou aluno!
void anuncio (pessoa& p)
{
p.anunciar();
}
Extensibilidade
Quando anunciar() é definida como virtual na classe base
(pessoa) torna-se possível adicionar tipos novos (derivados da
pessoa) sem necessidade de modificar a função anuncio().
Osaluno_pos_gr.h
programas tornam-se extensíveisaluno_pos_gr.cpp
porque podemos adicionar
void aluno_pos_gr::anunciar()
class aluno_pos_gr
: public
aluno
funcionalidades
novas
herdando
os
tipos
de dados novos de uma
{
{
public:
cout << "Sou aluno de "
classe
base
comum.
void anunciar ();
};
};
"pós-graduação!" << endl;
As funções que manipulam a interface da classe base não precisam
int main (int argc, char* argv[])
de{ ser modificadas para poderem suportar as classes novas!
aluno_pos_gr a2;
anuncio (a2);
}
return 0;
Sou aluno de pós-graduação!
aluno_pos_gr.h
class aluno_pos_gr : public aluno
{
public:
void anunciar ();
};
aluno_pos_gr.cpp
void aluno_pos_gr::anunciar()
{
cout << "Sou aluno de "
"pós-graduação!" << endl;
};
pessoa
aluno_doutoramento.h
class doutoramento : public aluno_pos_gr
{
aluno
};
aluno_pos_gr
aluno_doutoramento
int main (int argc, char* argv[])
{
aluno_doutoramento a3;
anuncio (a2);
}
return 0;
Sou aluno de pós-graduação!
Implementação da ligação dinâmica
Um compilador típico cria uma tabela (VTABLE) para cada classe
que contém funções virtuais.
Nesta tabela põem-se os endereços de todas as funções virtuais
desta classe particular.
Em cada objecto da classe com funções virtuais o compilador cria
um ponteiro (vpointer) que aponta para a VTABLE deste objecto.
Quando se efectua a chamada de uma função virtual através do
ponteiro para a classe base, o compilador gera código que permita
aceder ao vpointer e determinar o endereço da função em
VTABLE invocando deste modo a função correcta e causando a
ligação dinâmica.
class NoVirtual
{
int a;
public:
void x() const {}
int i() const { return 1; }
};
class OneVirtual
{
int a;
public:
virtual void x() const {}
int i() const { return 1; }
};
int: 4
NoVirtual: 4
void*: 4
OneVirtual: 8
TwoVirtual: 8
class TwoVirtuals
{
int a;
public:
virtual void x() const {}
virtual int i() const { return 1; }
};
int main()
{
cout <<
cout <<
cout <<
cout <<
cout <<
}
"int: " << sizeof(int) << endl;
"NoVirtual: " << sizeof(NoVirtual) << endl;
"void* : " << sizeof(void*) << endl;
"OneVirtual: " << sizeof(OneVirtual) << endl;
"TwoVirtuals: " << sizeof(TwoVirtuals) << endl;
return 0;
pessoa* A [] = { new pessoa, new aluno, new aluno_pos_gr,
new aluno_doutoramento, };
Array de
ponteiros
para pessoa:
Objectos:
pessoa
VTABLES:
&pessoa::anunciar
vptr
aluno
&aluno::anunciar
vptr
aluno_pos_gr
&aluno_pos_gr ::anunciar
vptr
aluno_doutoramento
vptr
&aluno_pos_gr ::anunciar
class pessoa
{
public:
};
virtual void anunciar ()
{
cout << "Sou pessoa!" << endl;
}
virtual void print_me();
virtual ~pessoa();
class aluno : public pessoa
{
public:
void anunciar ()
{
cout << "Sou aluno!" << endl;
}
void print_me();
~aluno();
};
ponteiro
para pessoa:
VTABLE:
Objecto:
aluno
&aluno :: anunciar
[0]
vptr
&aluno :: print_me
[1]
&aluno :: ~aluno
[2]
aluno a;
pessoa& pr = a;
não virtual:
chamar função pessoa::print_me
pr.print_me();
virtual:
chamar função no endereço VPTR[1]
mov ecx,dword ptr [ebp-14h]
call @ILT+160(pessoa::print_me) (004010a5)
mov
mov
mov
mov
call
cmp
call
ecx,dword ptr [ebp-14h]
edx,dword ptr [ecx]
esi,esp
ecx,dword ptr [ebp-14h]
dword ptr [edx+4]
esi,esp
__chkesp (004038d0)
O vpointer é inicializado no construtor da classe automaticamente.
O upcasting só funciona com os endereços. Quando o compilador
tem o objectos, ele conhece o tipo exacto e portanto não precisa da
ligação dinâmica.
int main()
{
aluno a;
a.anunciar();
pessoa& pr = a;
pr.anunciar();
ligação dinâmica
pessoa* pp = &a;
pp.anunciar();
ligação dinâmica
return 0;
}
ligação estática
Overhead
Download

ppt2