Vamos abordar o exemplo Assumimos que se pretende declarar uma classe que se chama “pessoa” (de notar que uma classe semelhante foi considerada na aula anterior): class pessoa Este é o construtor { unsigned short Idade; char* Nome; public: pessoa(unsigned short Id=0, char* No=""); virtual ~pessoa(); Este é o destrutor virtual void print_me(); const pessoa& operator =(const pessoa&); }; Esta funçãao será utilizada para a visualização dos dados no monitor Nós precisamos de usar esta função mas ela não é significativa neste contexto e por isso vai ser considerada posteriormente Vamos assumir que nós criámos uma classe derivada da classe pessoa, por exemplo: class aluno : public pessoa Este é o número do grupo do aluno { int grupo; public: void print_me(); Idade aluno(unsigned short, char*, int); Nome virtual ~aluno(); const aluno& operator =(const aluno& pes); }; Esta função será utilizada para a visualização dos dados no monitor A classe aluno foi derivada da classe pessoa Consideremos o seguinte programa constituído por duas funções main e print_all: O programa main realiza das seguintes acções: 1. Declara dois objectos, que são p do tipo pessoa e a do tipo aluno; 2. Executa a função p.print_me para o objecto p; 3. Declara array que é composto por ponteiros para objectos da classe pessoa int main(int argc, char* argv[]) 4. O primeiro “muito[0]” vai ser preenchido com { ponteiro pessoa p(25,"Paulo"); o valor &p que é o aluno ponteiro para o objecto6251); “p” do tipo pessoa. a(21, "Ricardo", p.print_me(); 5. O segundo ponteiro “muito[1]” vai ser preenchido com pessoa* muito[Quanto]; o valor &a que é o ponteiro para o objecto “a” do tipo aluno. muito[0]=&p; “Quanto” é uma muito[1]=&a; constante que, por exemplo, print_all(muito,Quanto); pode ser definida da return 0; seguinte forma: } 3 6. Executa a função print_all #define que vai Quanto ser considerada na próxima página int main(int argc, char* argv[]) { pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); p.print_me(); pessoa* muito[Quanto]; muito[0]=&p; muito[1]=&a; print_all(muito,Quanto); return 0; } void print_all(pessoa** ponteiro_para_pessoa, int size) { int i=0; while (i<size) { ponteiro_para_pessoa[i++]->print_me(); } } ** significa Array de ponteiros ponteiro_para_pessoa Podem ser dos objectos derivados da classe pessoa ponteiro0 ponteiro1 objecto do tipo pessoa objecto do tipo pessoa ponteiro2 objecto do tipo pessoa objecto do tipo pessoa etc. void print_all(pessoa** ponteiro_para_pessoa, int size) { int i=0; while (i<size) { ponteiro_para_pessoa[i++]->print_me(); } } Esta função imprime os dados para todos os objectos que podem ser determinados através dos respectivos ponteiros. Idade, Nome Idade, Nome, grupo Isto pode ser feito com a ajuda da função print_me Já vimos que o ponteiro_para_pessoa pode ser ou um ponteiro para pessoa ou um ponteiro para aluno que é o tipo derivado do tipo pessoa Vocês devem compreender que: 1. A variável ponteiro_para_pessoa[índice] é um nome que pode possuir valores diferentes. 2. Se ponteiro_para_pessoa[índice] tem um valor do ponteiro para o objecto do tipo pessoa, ele permite aceder a este objecto na memória, por exemplo: memória Nome Idade O objecto do tipo pessoa 3. Se ponteiro_para_pessoa tem um valor do ponteiro para o objecto do tipo aluno (que foi derivado da pessoa), ele permite o acesso a este objecto na memória, por exemplo: Nome Idade grupo Memória O objecto do tipo aluno void print_all(pessoa** ponteiro_para_pessoa, int size) { int i=0; while (i<size) { ponteiro_para_pessoa[i++]->print_me(); } } Vamos abordar o seguinte código: pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); p.print_me(); pessoa* muito[Quanto]; muito[0]=&p; muito[1]=&a; print_all(muito,Quanto); Idade, Nome Idade, Nome, grupo 1. i=0; 2. ponteiro_para_pessoa[0]; 3. print_me() para pessoa; 4. i=1; 5. ponteiro_para_pessoa[1]; 6. print_me() para aluno; Agora vamos considerar a função print_me: void pessoa::print_me() Nome = “Paulo” { cout << "Nome - " << Nome Idade = 25 << "; Idade - " << Idade << endl; } pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); void aluno::print_me() saltar na próxima linha { pessoa::print_me(); cout << "grupo - " << grupo << endl; grupo = 6251 } Nome - Paulo; Idade - 25 Nome - Ricardo; Idade - 21 grupo - 6251 Podemos concluir o seguinte: 1. A função print_all pode ser usada para a visualização no monitor dos dados de qualquer objecto da classe pessoa (ou da classe derivada da classa pessoa, por exemplo da classe aluno). 2. Se declaramos qualquer nova classe derivada da classe pessoa (por exemplo empregado) podemos imprimir os respectivos dados sem redefinição da função print_all. 3. Isto permite expandir as possibilidades do programa sem redefinição do seu código. Por outras palavras, podemos usar o mesmo código mesmo em tarefas novas . 4. Isto é impossível sem a utilização de herança. A relação da herança pode ser designada da seguinte forma gráfica: pessoa A classe derivada aluno A classe base A classe derivada empregado Isto significa que a classe aluno é derivada da classe pessoa Podemos usar as mesmas regras para definir a outra classe derivada, por exemplo empregado. O código desta classe pode ser: class empregado : public pessoa { unsigned long salario; public: empregado(unsigned short, char*, unsigned long); virtual ~empregado(); void print_me(); const empregado& operator =(const empregado& pes); }; void empregado::print_me() { pessoa::print_me(); cout << "salario - " << salario << endl; } Isto significa que a função print_me deve ser chamada para a classe pessoa. Esta notação chama-se full qualified name (qualificador de nome completo) void print_all(pessoa** ponteiro_para_pessoa, int size) { int i=0; while (i<size) { ponteiro_para_pessoa[i++]->print_me(); } } Vamos abordar o seguinte código: pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); p.print_me(); pessoa* muito[Quanto]; muito[0]=&p; muito[1]=&a; empregado e(29, "Nuno", 180000); muito[2]=&e; muito[2]=&e; print_all(muito,Quanto); Idade, Idade, Idade, Nome, Nome, Nome salario grupo 1. i=0; 2. ponteiro_para_pessoa[0]; 3. print_me() para pessoa; 4. i=1; 5. ponteiro_para_pessoa[1]; 6. print_me() para aluno; 7. i=2; 8. ponteiro_para_pessoa[2]; 9. print_me() para empregado; Nome - Paulo; Idade - 25 pessoa p(25,"Paulo"); Nome - Ricardo; Idade - 21 grupo - 6251 aluno a(21, "Ricardo", 6251); Nome - Nuno; Idade - 29 salário - 180000 empregado e(29, "Nuno", 180000); pessoa aluno empregado class pessoa { unsigned short Idade; unsigned short Idade; char* Nome; char* Nome; public: public: pessoa(unsignedshort shortId=0, Id=0,char* char*No=""); No=""); pessoa(unsigned ~pessoa(); ~pessoa(); virtualvoid voidprint_me(); print_me(); virtual constpessoa& pessoa&operator operator=(const =(constpessoa&); pessoa&); const }; class pessoa protected: { unsigned short Idade; unsigned short Idade; char* Nome; char* Nome; public: public: pessoa(unsigned pessoa(unsignedshort shortId=0, Id=0,char* char*No=""); No=""); ~pessoa(); ~pessoa(); virtual virtualvoid voidprint_me(); print_me(); const constpessoa& pessoa&operator operator=(const =(constpessoa&); pessoa&); }; A classe base classederivadas derivada e por isso Esta função pode ser redefinida em A classes foi declarada como uma função virtual class empregado : public pessoa { unsigned long salario; public: empregado(unsigned short, char*, unsigned long); ~empregado(); void print_me(); const empregado& operator =(const empregado& pes); }; Polimorfismo representa um conceito na teoria de tipos no qual um único nome (como uma declaração de uma variável) pode denotar objectos de várias classes que se relacionam através de uma superclasse comum. Qualquer objecto que possua esse nome está apto a responder a um conjunto comum de operações. O oposto de polimorfismo é monomorfismo, que se encontra em todas as linguagens que sejam strongly typed e statically bound Linguagens tipificadas (typed), como o Pascal, são baseadas no conceito de que funções, procedimentos e operandos possuem um único tipo. Estas linguagens dizem-se monomórficas, no sentido de que qualquer valor ou variável pode ser interpretado como tendo um e um só tipo. Linguagens de programação monomórficas podem contrastar com linguagens polimórficas, nas quais alguns valores e variáveis podem ter mais do que um tipo Um símbolo, por exemplo "+", pode ser definido para significar coisas diferentes. Chamamos a este conceito overloading. Em C++ podemos declarar funções com nomes iguais, desde que as suas invocações possam ser distinguidas pelas suas assinaturas, que consistem no número e tipo dos seus argumentos Herança sem polimorfismo é possível, mas certamente que não é muito útil. Em C++ o polimorfismo é expresso através dos conceitos de 1) funções virtuais e 2) overloading O polimorfismo existe quando as características de herança e ligação dinâmica interagem. É talvez a característica mais poderosa das linguagens de programação orientada por objectos. Polimorfismo e herança são características que distinguem a programação orientada por objectos da programação tradicional com tipos de dados abstractos. Esta possibilidade vaiabordar ser considerada posteriormente Vamos o exemplo: com mais detalhe pessoa &referência_to_pessoa void print_ref(pessoa &reference_to_pessoa) { reference_to_pessoa.print_me(); } TIPO& oo objecto objecto A notação o TIPO& (por exemplo pessoa &reference_to_pessoa &reference_to_pessoa ou ou pessoa pessoa& reference_to_pessoa reference_to_pessoa pessoa& ) significa referência para um valor do tipo TIPO. Uma referência é um nome alternativo para o objecto void print_ref(pessoa &reference_to_pessoa) { reference_to_pessoa.print_me(); print_me } Mas a invocação da função print_me depende do valor da A função print_ref podereference_to_pessoa ser usada da seguinte maneira: int main(int argc, char* argv[]) De notar que a função print_ref chama a mesma função print_me { pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); empregado e(29, "Nuno", 180000); p print_ref(p); a print_ref(a); Nome - Paulo; Idade - 25 e print_ref(e); } Nome - Ricardo; Idade - 21 grupo - 6251 Nome - Nuno; Idade - 29 salário ‘ 180000 Isto é polimorfismo void print_ref(pessoa &reference_to_pessoa) { reference_to_pessoa.print_me(); } int main(int argc, char* argv[]) { pessoa p(25,"Paulo"); aluno a(21, "Ricardo", 6251); empregado e(29, "Nuno", 180000); print_ref(p); print_ref(a); print_ref(e); } Sumário void pessoa::print_me() { cout << "Nome - " << Nome << "; Idade - " << Idade << endl; } void aluno::print_me() { pessoa::print_me(); cout << "grupo - " << grupo << endl; } void empregado::print_me() { pessoa::print_me(); cout << "salario - " << salario << endl; } Vamos considerar as duas seguintes funções que tem a mesmo nome void print_ref(pessoa &reference_to_pessoa) { reference_to_pessoa.print_me(); } void print_ref(int &i) { cout << "i = " << i << endl; } Isto é polimorfismo Mas o argumento da primeira função é do tipo pessoa e o argumento da segunda função é do tipo int e assumimos que temos o seguinte codico int main(int argc, char* argv[]) { pessoa p(25,"Paulo"); int integer=12345; print_ref(p); print_ref(integer); return 0; } Nome - Paulo; Idade - 25 i = 12345 Consideremos a seguinte função: int operator-(const pessoa& reference_to_pessoa2) { int temp=Idade - reference_to_pessoa2.Idade; return temp; } Vamos considerar o seguinte código: é o nome dachar* função intEste main(int argc, argv[]) { aluno a(21, "Ricardo", 6251); empregado e(29, "Nuno", 180000); int integer=12345, integer1=5; cout << "difference in integers === " << integer-integer1 << endl; esto cout << "difference in age === " << (e-a) << endl; return 0; } Isto é sobrecarga devai Esta função ser chamada operações e também polimorfismodifference in integers === 12340 difference in age === 8 é e-a O que é importante: 1. Perceber o que são paradigmas de programação. 2. Perceber mais detalhes sobre encapsulamento. 3. Perceber o que é herança. 4. Perceber o que é um polimorfismo. 5. Perceber as regras da linguagem C++. 6. Estar apto a construir programas triviais que utilizem encapsulamento, herança e polimorfismo.