Buscar

Programação Orientada a Objetos - Poli 2016 - C++ - Aula07 Herança e Polimorfismo II

Prévia do material em texto

Escola Politécnica da Universidade de São PauloEscola Politécnica da Universidade de São Paulo
Laboratório de Programação 
Orientada a Objetos para 
Engenharia Elétrica
Aula 7: Herança e Polimorfismo II
PCS3111
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
2
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
3
Polimorfismo na aula anterior
 Na aula passada, vimos que uma subclasse 
pode herdar métodos da superclasse e alterar 
seu comportamento.
 Nesta aula, vamos trabalhar mais 
profundamente o conceito de polimorfismo.
4
Polimorfismo
A palavra vem do grego:
πολύς, polys, muito
μορφή, morphē, forma
5
Morfeu é o deus grego do sono e dos sonhos, 
que podia assumir qualquer forma. 
6
Imagem: ©Trustees of the British Museum, cedida mediante licença CC BY-NC-SA 4.0
Não é este Morfeu ;)
7
Definição
Polimorfismo é a propriedade de um único nome ser 
usado com diversos significados.
Na programação, isso significa que, dependendo do 
contexto, o comportamento pode ser feito diferente.
Nome de quê? Comportamento de quem?
R: de variáveis, de classes, mas principalmente de funções!
Ao lado de encapsulamento, abstração e herança, 
polimorfismo é característica essencial da orientação a 
objetos!
8
Formas de polimorfismo
 Há quatro formas de polimorfismo nas linguagens 
de programação OO:
• Sobrecarga ou overloading: um mesmo nome é usado 
com diferentes comportamentos e diferentes argumentos;
• Redefinição ou overriding: uma classe derivada 
estabelece um comportamento diferente para um método 
herdado de sua classe base;
• Variáveis polimórficas: uma variável que pode assumir 
valores de diferentes tipos durante a execução.
• Templates: uma forma de criar modelos para classes 
parametrizando os tipos. (observação: essa última forma 
não será vista nesta aula!)
9
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
10
Sobrecarga (Overloading)
 Um termo sobrecarregado pode ser usado em diferentes 
contextos, com significados diferentes.
 O mesmo ocorre com a linguagem natural: 
11
A manga da camisa A fruta manga
 O contexto permite identificar o significado atribuído ao 
termo.
Com certeza você já percebeu que 
alguns operadores têm diferentes 
implementações!
12
 A sobrecarga é a forma já conhecida de polimorfismo! 
 Seja o operador +
 Em C++, o mesmo operador pode ser usado em 
diferentes contextos:
4 + 5 (adição de inteiros)
3.14 + 2.0 (adição em ponto flutuante)
“cata”+ “vento" (concatenação de strings!)
 Dá para imaginar que a implementação da função + é 
diferente para cada caso!
Sobrecarga
 A sobrecarga pode ser de operadores e também 
de funções. 
 C++ permite que o programador faça mais de 
uma definição para um mesmo nome dentro do 
mesmo escopo desde que as declarações 
tenham:
• assinaturas diferentes e
• implementações diferentes 
13
Como o compilador decide?
 Quando o compilador encontra uma função ou 
operador sobrecarregados, decide a 
implementação mais adequada com base na 
comparação das assinaturas dos métodos.
• Este processo é chamado de resolução da 
sobrecarga.
14
Assinatura
 Dentro do mesmo escopo, a assinatura dos 
métodos precisa ser diferente. 
 A assinatura é uma combinação do número e 
tipo dos argumentos e do tipo do retorno. 
 Não pode haver sobrecarga apenas variando o 
tipo de retorno.
15
Exemplo 01: Soma
16
1 #include <iostream>
2 using namespace std;
3 
4 int soma (int a) {
5 return a;
6 }
7 
8 int soma (int a, int b) {
9 return a + b;
10 }
11 
12 int soma (int a, int b, int c) {
13 return a + b + c;
14 }
15 
16 int main() {
17 // testando a sobrecarga por assinatura
18 
19 cout << "6 = " << soma (6) << endl;
20 cout << "6 + 6 = " << soma (6, 6) << endl;
21 cout << "6 + 6 + 6 = " << soma (6, 6, 6) << endl;
22 
23 return 0;
24 }
Construtores sobrecarregados
 É comum haver mais de um construtor para 
uma classe, variando-se o número de 
parâmetros. 
 No exemplo seguinte, considere uma classe 
Relogio, cujos objetos podem ser construídos 
de 4 diferentes formas.
17
Exemplo 02: Relogio
18
5 class Relogio {
6 public:
7 Relogio();
8 Relogio (int hora);
9 Relogio (int hora, int minuto);
10 Relogio (int hora, int minuto, int segundo);
11 void imprimir ();
12 virtual ~Relogio();
13 protected:
14 int hora;
15 int minuto;
16 int segundo;
17 private:
18 };
19 
Há 4 diferentes construtores!
19
6 Relogio::Relogio() {
7 hora = minuto = segundo = 0;
8 }
9 Relogio::Relogio (int hora) {
10 this->hora = hora;
11 this->minuto = this->segundo = 0;
12 }
13 
14 Relogio::Relogio (int hora, int minuto) {
15 this->hora = hora;
16 this->minuto = minuto;
17 this->segundo = 0;
18 }
19 
20 Relogio::Relogio (int hora, int minuto, int segundo) {
21 this->hora = hora;
22 this->minuto = minuto;
23 this->segundo = segundo;
24 }
25 
26 void Relogio::imprimir() {
27 cout << hora << ":" << minuto << ":" << segundo << endl;
28 }
29 
30 Relogio::~Relogio() {
31 //dtor
32 }
20
1 #include <iostream>
2 #include "Relogio.h"
3 
4 using namespace std;
5 
6 int main() {
7 Relogio *r1, *r2, *r3, *r4;
8 r1 = new Relogio ();
9 r2 = new Relogio (1);
10 r3 = new Relogio (1, 30);
11 r4 = new Relogio (1, 30, 45);
12 r1->imprimir();
13 r2->imprimir();
14 r3->imprimir();
15 r4->imprimir();
16 }
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
21
Redefinição (Overriding)
 A redefinição acontece quando uma classe 
derivada modifica um método que herdou da 
classe base.
 A função redefinida pela classe derivada tem a 
mesma declaração da classe base. Isto 
significa:
• O mesmo nome
• O mesmo tipo de retorno
• A mesma assinatura (lista de parâmetros)
22
Qual a diferença de sobrecarga?
 A redefinição só faz sentido no contexto da 
herança; a sobrecarga ocorre numa mesma 
classe;
 As assinaturas precisam ser as mesmas;
 Os métodos redefinidos podem ser combinados 
com os que os redefinem;
 Em geral, é resolvida em tempo de execução e 
não em tempo de compilação.
Exemplo 03: Pássaros
 Seja a classe Passaro
 Suponha que todos os pássaros cantem, cada 
um de sua forma.
24
classe Passaro
25
8 class Passaro {
9 protected:
10 bool emExtincao;
11 string corPredominante;
12 public:
13 Passaro();
14 ~Passaro();
15 void canta();
16 bool isEmExtincao();
17 string getCorPredominante();
18 
19 };
Passaro.h
Construtor da classe base
Método da classe base
26
1 #include "Passaro.h"
2 
3 Passaro::Passaro() {
4 this->emExtincao = false;
5 this->corPredominante = "cinza";
6 }
7 
8 Passaro::~Passaro() {}
9 
10 void Passaro::canta() {
11 cout << "Piu Piu Piu" << endl;
12 }
13 
14 bool Passaro::isEmExtincao () {
15 return this->emExtincao;
16 }
17 
18 string Passaro::getCorPredominante () {
19 return this->corPredominante;20 }
Passaro.cpp
classe Arara
27
9 class Arara: public Passaro {
10 public:
11 Arara();
12 ~Arara();
13 void canta ();
14 };
15 
Arara.h
Construtor da classe derivada
Método da classe derivada
28
1 #include "Arara.h"
2 
3 Arara::Arara() : Passaro() {
4 this->emExtincao = true;
5 this->corPredominante = "azul";
6 }
7 
8 Arara::~Arara() {
9 }
10 
11 void Arara::canta() {
12 cout << "A-RA-RA --- A-RA-RA" << endl;
13 }
Arara.cpp
O método da classe 
derivada redefine o 
método da classe 
base: o mesmo 
nome, a mesma 
assinatura, mas 
funções diferentes!
29
7 int main() {
8 
9 
10 Passaro *p = new Passaro ();
11 cout << "Passaro " << p->getCorPredominante()
12 << " em extincao? R: " << (p->isEmExtincao() ? “Sim" :
13 “Não") << endl;
14 p->canta();
15 
16 Arara *a = new Arara ();
17 cout << "Arara " << a->getCorPredominante()
18 << " em extincao? R: " << (a->isEmExtincao() ? “Sim" :
19 “Não") << endl;
20 
21 a->canta();
22 
23 delete p;
24 delete a;
25 return 0;
26 }
main.cpp
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
30
Ligação estática
Exemplo 4
Se uma arara é um pássaro também, o que deve 
acontecer neste caso?
31
13 int main() {
14 Passaro *p [3];
15 p[0] = new Passaro ();
16 p[1] = new Arara ();
17 p[2] = new Arara ();
18 
19 for (int i = 0; i < 3; i++) {
20 p[i]->canta();
21 }
22 
23 delete p[0];
24 delete p[1];
25 delete p[2];
26 return 0;
27 }
Por quê?
Porque o método canta é polimórfico na hierarquia. 
O compilador escolhe qual método ele vai acionar a partir do 
tipo da variável no código fonte, e no comando
p[i]->canta()
p é do tipo Passaro!
Isto é, o compilador decide qual método usar antes de executar 
o programa (em tempo de compilação)
Isto é chamado de ligação estática (static binding)!
Observe que a atribuição 
p[1] = new Arara (); 
é válida pelo princípio da substituição.
32
Ligação estática
(static binding)
Na ligação estática, as referências são resolvidas 
em tempo de compilação.
O compilador escolhe o método a partir do tipo 
declarado no código fonte.
33
Ligação dinâmica
(Virtual)
 Para que o compilador adie a decisão de qual 
método polimórfico usar até o momento da 
execução do programa, declaramos o método 
como virtual.
 Isso indica ao compilador que espere a 
execução do programa para então decidir o 
método a ser usado com base na classe a que 
pertence o objeto.
 Este efeito é chamado de ligação dinâmica 
(dynamic binding)
34
Exemplo 5
 Neste exemplo, o método canta é declarado 
como virtual.
35
8 class Passaro {
9 protected:
10 bool emExtincao;
11 string corPredominante;
12 public:
13 Passaro();
14 virtual ~Passaro();
15 virtual void canta();
16 bool isEmExtincao();
17 string getCorPredominante ();
18 };
O método da classe 
base é declarado 
como virtual, 
indicando que ele é 
polimórfico. 
No main (Exemplo 5)
36
6 
7 int main() {
8 Passaro *p [3];
9 p[0] = new Passaro ();
10 p[1] = new Arara ();
11 p[2] = new Arara ();
12 
13 for (int i = 0; i < 3; i++) {
14 p[i]->canta();
15 }
16 
17 for (int i = 0; i < 3; i++) {
18 delete p[i];
19 }
20 
21 return 0;
22 }
A declaração virtual na 
classe Passaro indica ao 
compilador que não 
resolva o método, mas 
prepare para a decisão 
ser tomada em tempo de 
execução.
Ligação dinâmica
(Dynamic binding)
Na ligação dinâmica, as referências são resolvidas em 
tempo de execução.
O compilador escolhe o método a partir do tipo obtido em 
tempo de execução. Para tanto, os métodos das 
superclasses têm que ser declarados virtual.
37
Destrutor virtual
 Observe que a declaração de um método como 
virtual, numa superclasse, é indicativa da 
intenção de herança e polimorfismo.
 O que acontece se não declararmos o destrutor 
dessa superclasse como virtual?
38
Destrutor virtual – Exemplo 06
39
6 
7 int main() {
8 Passaro *p [3];
9 p[0] = new Passaro ();
10 p[1] = new Arara ();
11 p[2] = new Arara ();
12 
13 for (int i = 0; i < 3; i++) {
14 p[i]->canta();
15 }
16 
17 for (int i = 0; i < 3; i++) {
18 delete p[i];
19 }
20 
21 return 0;
22 }
Se o destrutor de Passaro
não fosse virtual, qual dos 
destrutores seria chamado 
aqui?
O que aconteceria?
Destrutor virtual – Exemplo 06
40
6 
7 int main() {
8 Passaro *p [3];
9 p[0] = new Passaro ();
10 p[1] = new Arara ();
11 p[2] = new Arara ();
12 
13 for (int i = 0; i < 3; i++) {
14 p[i]->canta();
15 }
16 
17 for (int i = 0; i < 3; i++) {
18 delete p[i];
19 }
20 
21 return 0;
22 }
Se o destrutor de Passaro
não fosse virtual, qual dos 
destrutores seria chamado 
aqui?
O que aconteceria?
Implementação dos destrutores
(Exemplo 6)
41
3 Passaro::Passaro() {
4 this->emExtincao = false;
5 this->corPredominante = "cinza";
6 }
7 
8 Passaro::~Passaro() {
9 cout << "Adeus Passaro! ";}
10 
11 void Passaro::canta() {
12 cout << "Piu Piu Piu" << endl;
13 }
14 
15 bool Passaro::isEmExtincao () {
16 return this->emExtincao;
17 }
18 
19 string Passaro::getCorPredominante (){
20 return this->corPredominante;
21 }
3 Arara::Arara() : Passaro() {
4 this->emExtincao = true;
5 this->corPredominante = "azul";
6 }
7 
8 Arara::~Arara() {
9 cout << "Adeus Arara! " << endl;
10 }
11 
12 void Arara::canta() {
13 cout << "A Arara canta A-RA-RA ---
A-RA-RA" << endl;
14 }
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
42
Substituição x refinamento
 Quando um método é redefinido, o código no 
método da classe derivada pode:
• Substituir completamente o código do método da 
classe base (substituição)
ou
• Chamar o método da classe base e acrescentar a ele 
o código específico da classe derivada (refinamento)
43
Construtores sempre usam refinamento 
 O construtor da classe base é acionado quando 
construímos a classe derivada.
 Desta forma garante-se que toda inicialização 
da classe base acontece também para os 
objetos da classe derivada.
44
Exemplo de refinamento
45
3 Arara::Arara() : Passaro() {
4 this->emExtincao = true;
5 this->corPredominante = "azul";
6 }
7 
8 Arara::~Arara() {
9 cout << "Adeus Arara! " << endl;
10 }
11 
12 void Arara::canta() {
13 cout << "A Arara canta A-RA-RA --- A-RA-RA" << endl;
14 }
O construtor da 
classe derivada 
refina o método da 
classe base, neste 
caso inicializando de 
forma diferente os 
parâmetros. 
Agenda
1. Polimorfismo
2. Sobrecarga
3. Redefinição
4. Ligação estática e ligação dinâmica
5. Refinamento
6. Variáveis polimórficas
46
47
1 #include <iostream>
2 
3 using namespace std;
4 class A {
5 };
6 
7 class B: public A {
8 };
9 
10 int main() {
11 A *a = new A();
12 delete a;
13 
14 a =new B();
15 delete a;
16 }
A variável a é polimórfica, pois 
recebe objetos de classes 
diferentes. 
Variáveis também podem ser 
polimórficas!
this
 this é a variável polimórfica mais comum!
 Ela pode receber diferentes classes!
48
Conclusões
Polimorfismo é importante porque
 Favorece a componentização e 
consequentemente, o reúso de software, na 
medida em que um comportamento básico pode 
ser estendido para atender às peculiaridades 
das classes derivadas.
 Favorece a abstração e o encapsulamento, na 
medida em que nomes iguais escondem a 
implementação diferente.
49
Bibliografia
 Budd, T. An Introduction to Object-Oriented
Programming. Addison-Wesley, 3rd ed. 2002.
• Conceito de polimorfismo: Capítulo 14
• Sobrecarga: Capítulo 15
• Redefinição: Capítulo 16
• Variáveis polimórficas: Capítulo 17
 Lafore, R. Object Oriented Programming in C++. 
Sams Publishing, 4th. ed. 2002.
• Sobrecarga de operadores: Capítulo 8
• Herança: Capítulo 9
50

Outros materiais

Materiais relacionados

Perguntas relacionadas

Perguntas Recentes