AULA 14 - Programação II - Graduação

De IFSC
Revisão de 07h46min de 16 de maio de 2018 por imported>Fargoud
Ir para navegação Ir para pesquisar

FUNÇÕES VIRTUAIS E POLIMORFISMO: Em muitas situações, projeta-se uma classe base que vai gerar muitas classes derivadas, diferentes entre si... Como desenvolver os métodos na super-classe, de forma bastante genérica, para que cada classe possa particularizá-los na medida do que for necessário?  FUNÇÕES VIRTUAIS

Exemplo: temos que definir a superclasse FormaGeometrica, a partir da qual serão derivadas as classes Circulo, Retangulo, Triangulo e Elipse. O método Desenha() certamente será bastante diferente, em cada uma das subclasses.....???  Definir o método como virtual na superclasse e redefinir o método em cada uma das subclasses!

Outro Exemplo: Deseja-se criar um vetor de ponteiros para acessar objetos....

  1. include <iostream.h>

class base // Classe base { public: void print( ) { cout << “\nBASE”;} }; class deriv0: public base //Classe derivada de base { public: void print( ) { cout << “\nDERIV0”;} }; class deriv1: public base //Classe derivada de base { public: void print( ) { cout << “\nDERIV1”;} }; class deriv2: public base //Classe derivada de base { public: void print( ) { cout << “\nDERIV2”;} };


void main() { base * ptr[3]; // cria vetor de ponteiros para classe base

deriv0 dv0; // cria objeto da subclasse deriv0

    	deriv1 dv1; // cria objeto da subclasse deriv1

deriv2 dv2; // cria objeto da subclasse deriv2

        // preenche vetor de ponteiros: 

ptr[0] = &dv0; ptr[1] = &dv1; ptr[2] = &dv2;

for(int i = 0; i < 3; i++) ptr[i]->print( ); // chama método }

Para resolver o problema:....

  1. include <iostream.h>

class base // Classe base {public: virtual void print( ) { cout << “\nBASE”;} //MÉTODO VIRTUAL }; ... O restante do código permanece exatamente igual e a saída agora fica:


Quando a função virtual não possui código próprio é chamada de função virtual pura, ou seja, foi criada apenas para ser redefinida nas classes derivadas - INTERFACE POLIMÓRFICA PARA SUBCLASSES. Exemplo: class base // Classe base { public: virtual void print( ) = 0; // função virtual pura }; ... A classe que possui uma ou mais funções virtuais puras não pode gerar objetos diretamente e é chamada de classe ABSTRATA - só pode ser utilizada como superclasse!



6.8. Herança virtual

Considere a derivação da classe D ilustrada a seguir:

Se a classe A possui um dado membro a, um objeto da classe D herdará duas cópias desse dado, uma vez da classe B e outra da classe C. Para acessar esses dados duplicados, será preciso empregar o operador de resolução de escopo.

Exemplo 6.15: void main(void) { D d; d.a = 0; // erro: ambíguo d.B::a = 1; // Ok, acessa cópia herdada de B d.C::a = 2; // Ok, acessa cópia herdade de C } A B D C Herança 41

Através da herança virtual, entretanto, é possível especificar que haja apenas uma ocorrência dos dados herdados da classe base. Para que a classe D herde apenas uma cópia dos dados da classe A, é preciso que as classes B e C herdem virtualmente de A. Exemplo 6.16: class B : virtual public A { ... }; class C : virtual public A { ... }; class D : public B, public C { ... }; void main() { D d; d.a = 0; // Ok, não há mais ambigüidade } É preciso não confundir esse status "virtual" da declaração de herança de classes com aquele dos membros virtuais que veremos mais adiante. Aqui, a palavra chave virtual apenas indica ao compilador que os dados herdados não deverão ser duplicados. 6.9. Polimorfismo e funções virtuais A herança nos permite reutilizar numa classe derivada um código escrito para uma classe base. Nesse processo, alguns métodos podem ser redefinidos e, embora tenham nomes idênticos, ter implementações completamente distintas. Graças ao polimorfismo, podemos usar uma mesma instrução para chamar, dinamicamente, métodos completamente distintos numa hierarquia de classes. Em C++, o polimorfismo é obtido através de funções virtuais. Se uma classe base tem uma função virtual f() e sua classe derivada D também tem uma função f(), do mesmo tipo, então uma chamada a f() a partir de um objeto da classe derivada estará invocando D::f(); mesmo que o acesso seja feito através de um ponteiro ou de uma referência. A função da classe derivada sobrescreve a função da classe base; mas, se os tipos das funções forem diferentes, as funções são consideradas distintas e o mecanismo virtual não é empregado. Exemplo 6.17: class Componente { public: void exibe() const { cout << "Componente::exibe()" << endl; } }; class Botao: public Componente { public: void exibe() const { cout << "Botao::exibe()" << endl; } }; class Janela: public Componente { public: void exibe() const { cout << "Janela::exibe()" << endl; } }; void manipula(const Componente &c) { c.exibe(); } Herança 42 void main() { Botao ok; Janela ajuda; manipula(ok); manipula(ajuda); } Executando o programa acima, veremos que a instrução c.exibe(), na função de manipulação, chamará o método exibe() da classe Componente. Mas isso, na prática, seria um erro; pois a exibição de um botão é uma tarefa diferente da exibição de uma janela. Se na função de manipulação queremos chamar o método exibe() correspondente à classe a que pertence o objeto passado à função, então temos que definir, dentro da classe base, o método exibe() como virtual. Exemplo 6.18: class Componente { public: virtual void exibe() const { cout << "Componente::exibe()" << endl; } }; Para maior clareza, a palavra chave virtual pode ser repetida também na declaração do método exibe() nas classes derivadas. Exemplo 6.19: class Botao: public Componente { public: virtual void exibe() const { cout << "Botao::exibe()" << endl; } }; class Janela: public Componente { public: virtual void exibe() const { cout << "Janela::exibe()" << endl; } }; Ao encontrar uma chamada a um método virtual, o compilador terá que aguardar até o momento da execução para decidir qual o método correto a ser chamado. Quando trabalhamos com ponteiros para objetos de classes derivadas, é importante que os destrutores também sejam declarados virtuais. Se um objeto da classe derivada está sendo apontado por ponteiro da classe base, e os destrutores são virtuais, então, quando esse objeto é liberado, primeiro será executado o destrutor da classe derivada e só depois o da classe base. Exemplo 6.20: class Componente { public: virtual ~Componente() { cout << "Destrói componente" << endl; } }; class Botao: public Componente { public: ~Botao() { cout << "Destrói botao" << endl; } }; Herança 43 void main(void) { Botao *ok = new Botao; Componente *c = ok; delete c; } Ao ser executado o comando delete, no programa acima, serão exibidas as mensagens "Destrói botão" e "Destrói componente". Porém, se o destrutor não tivesse sido declarado virtual, apenas o destrutor da classe Componente, aquela a que pertence o ponteiro, seria chamado. Nem construtores nem métodos estáticos podem ser declarados virtuais. Além disso, a acessibilidade de um método virtual é conservada dentro de todas as classes derivadas, mesmo que ele seja redefinido com um status diferente . 6.10. Classes abstratas Às vezes, um método virtual definido em uma classe base, serve como uma interface genérica, que deverá ser implementada nas classes derivadas. Se não há implementação desse método na classe base, dizemos que tal método é virtual puro. Para indicar que um método é virtual puro, adicionamos o sufixo = 0 ao seu protótipo. Exemplo 6.21: class Componente { public: virtual void exibe() const = 0; // método virtual puro }; void main() { Componente c; // erro, não se pode instanciar uma classe abstrata! ... } Uma classe é abstrata se contém pelo menos um método virtual puro. Não é possível criar uma instância de uma classe abstrata. Além disso, uma classe abstrata não pode ser usada como tipo de argumento nem como tipo de retorno de uma função. Um método virtual puro não precisa ter uma implementação. Além disso, uma classe abstrata somente pode ser usada a partir de um ponteiro ou de uma referência. Uma classe derivada que não redefine um método virtual é também considerada abstrata.




Funções Amigas << AULA 14 - Funções Virtuais e Polimorfismo Streams >>

<<= Página do curso