Aula 7 Herança 1 Programação Orientada para Objectos Conceitos fundamentais: 2 Herança Polimorfismo Ligação dinâmica 2003/2004 Programação Orientada para Objectos Empregado class Empregado { public: Empregado(string const& nome, Sexo const sexo); string const& nome() const; Sexo sexo() const; void mostra() const; private: string nome_; Sexo sexo_; }; inline Empregado::Empregado(string const& nome, Sexo const sexo) : nome_(nome), sexo_(sexo) {} 3 2003/2004 Programação Orientada para Objectos Empregado inline string const& Empregado:: nome() const { return nome_; } inline Sexo Empregado:: sexo() const { return sexo_; } inline void Empregado:: mostra() const { cout << "Nome: " << nome() << endl << "Sexo: " << sexo() << endl; } 4 2003/2004 Programação Orientada para Objectos Chefe class Chefe { public: Chefe(string const& nome, Sexo const sexo, int const nível); string const& nome() const; Sexo sexo() const; int nível() const; void mostra() const; private: string nome_; Sexo sexo_; int nível_; }; inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível) : nome_(nome), sexo_(sexo), nível_(nível) { } 5 2003/2004 Programação Orientada para Objectos Chefe inline string const& Chefe::nome() const { return nome_; } inline Sexo Chefe::sexo() const { return sexo_; } inline int Chefe::nível() const { return nível_; } inline void { cout << << << } 6 Chefe::mostra() const "Nome: " << nome() << endl "Sexo: " << sexo() << endl "Nível: " << nível() << endl; 2003/2004 Programação Orientada para Objectos Problemas Repetição de código Não há relação explícita entre as duas classes (o que é conceptualmente falso) 7 Quase tudo igual entre Empregado e Chefe Empregado e Chefe são totalmente independentes, embora se saiba que um chefe é um empregado! 2003/2004 Programação Orientada para Objectos Instanciação vs. especialização Eu sou um humano Instanciação Um humano é um mamífero Especialização (ou generalização) 8 Relação entre uma instância ou objecto e a sua classe: eu sou um objecto da classe dos humanos Relação entre duas classes: os humanos são mamíferos 2003/2004 Programação Orientada para Objectos Conceitos e implementações Língua natural C++ Classe: Humano Nome comum: “humano” Definida como: class Humano { // ... }; Nome próprio: "Zé" "O Zé é um humano." 9 Variável: zé Definição de variável: Humano zé; 2003/2004 Programação Orientada para Objectos Relação de generalização Empregado Atributos e operações omitidos. Relação de generalização. Chefe 10 2003/2004 Programação Orientada para Objectos Impacto da modelação da relação Corte (ou slicing) Chefe ana_maria(“Ana Maria”, feminino, 4); Empregado sósia_da_ana_maria_como_empregado = ana_maria; 11 Cópia da Ana Maria cortando tudo aquilo que a torna chefe. Não é muito útil: a evitar! 2003/2004 Programação Orientada para Objectos Impacto da modelação da relação list<Empregado*> empregados; empregados.push_back(new Empregado("João Maria", masculino)); empregados.push_back(new Chefe("Ana Maria", feminino, 4)); ... for(list<Empregado*>::const_iterator i = empregados.begin(); i != empregados.end(); ++i) { (*i)->mostra(); cout << endl; } 12 2003/2004 Programação Orientada para Objectos Representação da lista em UML pessoal: list<Empregado*> ! 13 : Empregado* : Empregado* : Empregado : Chefe nome_ = “João Maria” sexo_ = masculino nome_ = “Ana Maria” sexo_ = feminino nível_ = 4 2003/2004 ! Ligação. Programação Orientada para Objectos Impacto da modelação da relação: Resultado desejado Nome: João Maria Sexo: masculino Nome: Ana Maria Sexo: feminino Nível: 4 14 2003/2004 Programação Orientada para Objectos Solução 1: distinção interna do tipo de Empregado class Empregado { public: enum Tipo {empregado, chefe}; Empregado(Tipo const tipo, string const& nome, Sexo const sexo, int const nível = 0); string const& nome() const; Sexo sexo() const; int nível() const; void mostra() const; 15 2003/2004 Programação Orientada para Objectos Solução 1: distinção interna do tipo de Empregado private: string nome_; Sexo sexo_; int nível_; Tipo tipo; } inline void Empregado::mostra() const { cout << “Nome: ” << nome() << endl << “Sexo: ” << sexo() << endl; if(tipo == chefe) cout << “Nível: “ << nível() << endl; } 16 2003/2004 Uma só classe representa todos os tipos de empregados. Programação Orientada para Objectos Problemas da solução 1 Construtor teria de prever todos os casos e verificar todas as combinações impossíveis. Todos os métodos com especializações para cada tipo de empregado seriam monstruosos, dominados por inúmeros e gigantescos switch… Código: 17 difícil de compreender difícil de estender difícil de alterar difícil de depurar 2003/2004 Programação Orientada para Objectos Solução 2: um Empregado dentro de um Chefe class Chefe { public: Chefe(string const& nome, Sexo const sexo, int const nível); string const& nome() const; Sexo sexo() const; int nível() const; void mostra() const; private: Empregado empregado; int nível_; }; 18 2003/2004 Programação Orientada para Objectos Solução 2: um Empregado dentro de um Chefe ... inline void Chefe::mostra() const { empregado.mostra(); cout << "Nível: " << nível() << endl; } Duas classes: • Chefe delega em empregado. • Permite poupar código. 19 2003/2004 Programação Orientada para Objectos Problemas da solução 2 20 Ainda não é possível tratar um chefe como se de um empregado se tratasse Dá a sensação que os chefes possuem fisicamente um pequenino homúnculo dentro deles encarregue de trabalhar por eles... O nosso código não corresponde ao que queremos modelar, o que é sempre má ideia 2003/2004 Programação Orientada para Objectos Solução: herança pública class Chefe : public Empregado { public: Chefe(string const& nome, Sexo const sexo, int const nível); int nível() const; void mostra() const; private: int nível_; }; inline int Chefe::nível() const { return nível_; } 21 2003/2004 Programação Orientada para Objectos Solução: herança pública Ocultação (e especialização do método da classe base) inline void Chefe::mostra() const { Empregado::mostra(); cout << "Nível: " << nível() << endl; } Construtor inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível) : ???? {} 22 2003/2004 Programação Orientada para Objectos Solução: herança pública Ocultação (e especialização do método da classe base) inline void Chefe::mostra() const { Empregado::mostra(); cout << "Nível: " << nível() << endl; } Construtor inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível) : nome_(nome), sexo_(sexo), nível_(nível) {} 23 2003/2004 Programação Orientada para Objectos Solução: herança pública Ocultação (e especialização do método da classe base) inline void Chefe::mostra() const { Empregado::mostra(); cout << "Nível: " << nível() << endl; } Construtor inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível) : Empregado(nome, sexo), nível_(nível) {} 24 2003/2004 Programação Orientada para Objectos Construção e destruição 25 Ordem de construção: Primeiro as classes base, por ordem de declaração no cabeçalho da classe derivada Depois os atributos, por ordem de declaração no corpo da classe Finalmente é executado o corpo do construtor Destruição ocorre pela ordem inversa! 2003/2004 Programação Orientada para Objectos Construção e destruição class Z { // ... }; class D : public A, public B { public: D(int i, Z const& z); ~D(); ... private: static int const dim = 10; int i; Z const z; float m[dim]; int* pi; }; 26 D::D(int i, Z const& z) : i(i), z(z), pi(new int(10)) { for(int i = 0; i != dim; ++i) m[i] = 0; } D::~D() { delete pi; } 2003/2004 Programação Orientada para Objectos Ordem de construção Construtor de A, para inicialização dos atributos herdados de A Construtor de B, para inicialização dos atributos herdados de B Construtor de int, para inicialização do atributo i Construtor de Z, para inicialização do atributo constante z Construtor de pi, para inicialização do atributo pi com o endereço de uma nova variável dinâmica Atributos de tipos básicos ou deles derivados não têm quaisquer construtores invocados implicitamente (é o caso do atributo m) Corpo do construtor, onde se inicializa a matriz m 27 2003/2004 Programação Orientada para Objectos Ordem de destruição Primeiro é executado o corpo do destrutor Depois são invocados os destrutores de todos os atributos de instância da classe derivada, por ordem inversa de declaração Finalmente são invocados os destrutores de todas as classes base por ordem inversa à do seu aparecimento na especificação das heranças na definição da classe derivada 28 2003/2004 Programação Orientada para Objectos Conceitos e implementações Língua natural C++ Classe: Humano Nome comum: “humano” Definida como: class Humano {}; Nome próprio: "Zé" "O Zé é um humano." "Um humano é um mamífero", ou "Qualquer humano é um mamífero". 29 Variável: zé Definição de variável: Humano zé; Derivação de classe: class Humano : public Mamífero { }; 2003/2004 Programação Orientada para Objectos Herança pública: questões Qual é a base e qual é a derivada? Qual o sentido (direcção) da relação é um? Categorias de acesso Ambas têm método mostra(). Que significa? 30 O que é privado na classe base não pode ser mexido na classe derivada O que é público na classe base pode ser mexido por toda a gente, mesmo a classe derivada Classe derivada tem acesso directo ao nome? Classe derivada herdou da classe base, acrescentou inspector para o nível e tem de especializar método mostra() Fornece versão, ocultando e especializando a versão da classe base 2003/2004 Programação Orientada para Objectos Herança pública: questões class D : public A, public B, public C { ... }; Cria nova classe D derivada das classes base A, B e C Classe derivada herda todos os membros das classes base Membros privados da classe base não ficam acessíveis directamente a partir dos métodos da classe derivada 31 Se não fosse assim, estar-se-ia a abrir a porta da parte privada de uma classe a quem quer que definisse uma classe sua derivada, violando-se com isso o princípio do encapsulamento 2003/2004 Programação Orientada para Objectos Herança pública A herança ou derivação fez-se usando a palavra chave public: 32 Membros públicos da classe base são herdados como membros públicos da classe derivada (daí que não seja necessário redefinir as operações nome() e sexo()) Quaisquer instâncias da classe derivada são compatíveis com instâncias da classe base, o mesmo se passando com ponteiros e referências 2003/2004 Programação Orientada para Objectos Herança em C++ Possibilita Chefe o_zé("Zé Maria", masculino, 4); Empregado& de_novo_o_zé = o_zé; Empregado* ponteiro_para_o_zé = &zé; ou void pagaSalário() // capaz de lidar com qualquer tipo específico de empregado. { // ... } 33 2003/2004 Programação Orientada para Objectos Solução: completa? Problema ainda não resolvido: list<Empregado*> empregados; empregados.push_back(new Empregado("João Maria", masculino)); empregados.push_back(new Chefe("Ana Maria", feminino, 4)); // ... for(list<Empregado*>const_iterator i = empregados.begin(); i != empregados.end(); ++i) { (*i)->mostra(); cout << endl; } 34 2003/2004 Programação Orientada para Objectos Diagrama UML pessoal: list<Empregado*> ! 35 : Empregado* : Empregado* : Empregado : Chefe nome_ = “João Maria” sexo_ = masculino nome_ = “Ana Maria” sexo_ = feminino nível_ = 4 2003/2004 ! Ligação. Programação Orientada para Objectos Solução: incompleta Nome: João Maria Sexo: masculino Nome: Ana Maria Sexo: feminino 36 Método mostra() executado aquando da invocação da operação mostra() depende do ponteiro através do qual se faz a invocação e não do tipo do objecto apontado! 2003/2004 Programação Orientada para Objectos Solução: incompleta Ligação entre operação invocada e método executado é estática (decidida pelo compilador) Falta polimorfismo 37 Permite que operações realizadas sobre objectos apontados por ponteiros de um único tipo terem comportamentos diversos consoante o objecto apontado Falta ligação dinâmica entre a operação invocada e o método executado (decidida durante a execução do programa) 2003/2004 Programação Orientada para Objectos Hierarquias de classes Veículo VeículoTerrestre VeículoAéreo Helicóptero VeículoNaval Avião Hidroavião 38 2003/2004 Programação Orientada para Objectos Herança privada (I) Métodos públicos da classe base tornam-se privados da classe derivada! Não é estabelecida relação é um entre a classe derivada e a classe base Estabelece tipo especial de relação: funciona como um… mas… 39 Conceito a implementar diferente de outro com pequenas variações (normalmente, restrições da interface) 2003/2004 Programação Orientada para Objectos Herança privada (II) class PilhaDeInt { public: typedef ListaDeInt::Item Item; class PilhaDeInt : private ListaDeInt { public: typedef ListaDeInt::Item Item; int altura() const; bool estáVazia() const; int altura() const; bool estáVazia() const; Item const& topo() const; Item const& topo() const; Item& topo(); Item& topo(); void põe(Item const& novo_item); void tiraItem(); private: ListaDeInt lista; }; 40 void põe(Item const& novo_item); void tiraItem(); }; 2003/2004 Programação Orientada para Objectos Herança privada (III) class PilhaDeInt : private ListaDeInt { public: using ListaDeInt::Item; class PilhaDeInt : private ListaDeInt { public: using ListaDeInt::Item; int altura() const; using ListaDeInt::comprimento; using ListaDeInt::estáVazia; using ListaInt::estáVazia; using ListaDeInt::trás; Item const& topo() const; using ListaDeInt::põeAtrás; using ListaDeInt::tiraDeTrás; Item& topo(); }; void põe(Item item); void tiraItem(); }; 41 2003/2004 Programação Orientada para Objectos Conceitos e implementações: relações entre classes Língua natural C++ Associação simples: “Os empregados têm (estão associados a) um chefe.” class Empregado { private: Chefe* chefe; }; Agregação: “Uma turma tem (agrega) alunos.” class Turma { private: list<Aluno*> alunos; }; Composição: “Um humano tem (é composto por) cabeça.” 42 class Humano { private: Cabeça cabeça; }; 2003/2004 Não há conceito de posse: tempos de vida independentes Relação possui um: pode implicar o controlo do tempo de vida de um objecto por outro Relação é composto por um: tempos de vida relacionados Programação Orientada para Objectos Aula 7: Sumário 43 Herança pública: relação é um. A relação tem um. A relação é composto por um. Ponteiros, referências e corte (slicing). Derivação: classe base e classe derivada. Construtores e destrutores com herança. Ocultação. Hierarquias de classes: derivações de derivações. Possibilidade de herança múltipla. Problemas por resolver: necessidade de polimorfismo. Herança privada: redução/alteração de interfaces. Políticas de acesso. 2003/2004 Programação Orientada para Objectos