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