Apostila - Programa--o C++
169 pág.

Apostila - Programa--o C++


DisciplinaAnálise Textual9.567 materiais293.978 seguidores
Pré-visualização50 páginas
funções-membro precisam ser redefinidas em cada uma das classes derivadas. Suponha o seguinte 
exemplo, onde temos uma classe base e duas classes derivadas. A função print() está definida 
na classe base e redefinida nas classes derivadas. As nossas classes definem os tipos de clientes em 
um banco. Temos os clientes normais (classe-base) e clientes universitários ou especiais. 
 
// Testando funcoes Virtuais 
#include <iostream> 
using namespace std; 
class Cliente { 
 public: 
 virtual void print() { cout << &quot;\nCliente&quot;; } 
}; 
class Universitario : public Cliente { 
 public: 
 void print() { cout << &quot;\nCliente Universitario&quot;; } 
}; 
class Especial : public Cliente { 
 public: 
 void print() { cout << &quot;\nCliente Especial&quot;;} 
}; 
int main(int argc, char* argv[]) 
{ 
 Cliente * p[3]; // matriz de ponteiros da classe-base 
 Cliente B; 
 Universitario D1; 
 Especial D2; 
 p[0] = &B; 
 p[1] = &D1; 
 p[2] = &D2; 
 p[0]->print(); 
 p[1]->print(); 
 p[2]->print(); 
 
 return 0; 
} 
Planejamos agrupar um conjunto de objetos destas classes em uma lista única. Isto é, o 
nosso objetivo é ter uma lista única de clientes e diferenciá-los pelos tipos de objetos que os 
definem. Um dos meios é criar uma matriz de ponteiros para os diferentes objetos envolvidos. Qual 
seria o tipo da matriz tendo em vista que ela deve armazenar ponteiros para objetos de classes 
diferentes? A resposta está em como o compilador opera ponteiros em situações deste tipo. Um 
ponteiro para um objeto de uma classe derivada é de tipo compatível com um ponteiro para um 
objeto da classe-base. Segundo este princípio, criamos um vetor de ponteiros da classe-base e 
atribuímos a ele endereços das classes derivadas, como você pode observar acima. 
Podemos imprimir os dados da lista, através da chamada da função print() : 
Curso de Linguagem Computacional C/C++ 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 133 
for(int i = 0; i<10; i++) p[i]->print(); 
 
Observe que desejamos que funções completamente diferentes sejam executadas por meio 
da mesma instrução de chamada. Se o ponteiro p[i] apontar para um cliente normal, a função da 
classe base deverá ser executada; se p[i] apontar para um cliente especial, a função da classe de 
clientes especiais deverá ser executada. 
A característica de chamar funções membros de um objeto sem especificar o tipo exato do 
objeto é conhecida como polimorfismo. A palavra polimorfismo significa \u201cassumir várias formas\u201d. 
Em C++ indica a habilidade de uma única instrução chamar diferentes funções e portanto assumir 
diferentes formas. 
Para que o exemplo anterior funcionasse corretamente, duas condições tiveram que ser 
introduzidas. Em primeiro lugar, todas as diferentes classes de clientes devem ser derivadas da 
mesma classe-base. Em segundo lugar, a função print() deve ser declarada como virtual na 
classe-base. 
Apesar de termos fornecidos os endereços de objetos de classes derivadas, o compilador 
trata todos os endereços da lista de ponteiros como endereços da classe-base, ou seja, considera que 
os ponteiros apontam para objetos da classe-base. Quando executamos uma função utilizando este 
ponteiro, 
 p[i]->print(); 
 
a função será executada como se o objeto referenciado fosse da classe base. 
 p[i]->Cliente::print(); 
 
o compilador ignora o conteúdo do ponteiro p[i] e usa o seu tipo para identificar a função-
membro a ser chamada. Para acessar objetos de diferentes classes usando a mesma instrução, 
devemos declarar as funções da classe-base que serão reescritas em classes derivadas usando a 
palavra-chave virtual. 
As instruções de chamada a funções não-virtuais são resolvidas em tempo de compilação e 
são traduzidas em chamadas a funções de endereços fixos. Isto faz com que a instrução seja 
vinculada à função antes da execução. Quando uma instrução de chamada a uma função virtual é 
encontrada pelo compilador, ele não tem como identificar qual é a função associada em tempo de 
compilação. Em instruções como a escrita acima, o compilador não conhece qual classe p[i] contém 
antes de o programa ser executado. Então, a instrução é avaliada em tempo de execução, quando é 
possível identificar qual o tipo de objeto é apontado por p[i]. Isto é chamado de resolução dinâmica. 
A resolução dinâmica permite que uma instrução seja associada a uma função no momento 
de sua execução.O programador especifica que uma determinada ação deve ser tomada em um 
objeto por meio de uma instrução. O programa, na hora da execução, interpreta a ação e vincula a 
ação à função apropriada. A resolução dinâmica envolve mais memória e tempo de execução, 
entretanto aumenta a flexibilidade no projeto do software e permite criar bibliotecas de classes que 
outros programadores podem estender não tendo o arquivo-fonte. 
Uma função virtual pura é uma função virtual sem bloco de código ou o bloco de código não 
contém nenhuma instrução. O propósito de uso de uma função virtual pura é em situações em que a 
função nunca será executada e está presente somente para que seja redefinida em todas as classes 
derivadas. A função serve somente para prover uma interface polimórfica para as classes derivadas. 
A função print() pode ser definida como virtual pura da seguinte forma: 
 virtual void print() =0; 
 
O sinal de igual (=) e o valor 0 não têm nenhum efeito aqui. A sintaxe (=0) é usada somente 
para indicar ao compilador que esta é uma função virtual pura, e não tem corpo. 
Curso de Linguagem Computacional C/C++ 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 134 
Uma classe que não pode ser utilizada para criar objetos é dita classe abstrata e existe 
somente para ser usada como base para outras classes. A classe que define uma função virtual pura 
é uma classe abstrata pois não se pode declarar nenhum objeto dela. Entretanto, um ponteiro para 
uma classe abstrata pode ser declarado para manipular objetos de classes derivadas. Toda classe que 
contém pelo menos uma função virtual pura ou uma classe derivada que não redefine a função 
virtual pura de sua classe-base é abstrata. 
17.2 Destrutores Virtuais 
Se destrutores são definidos na classe-base e na classe derivada, eles são executados na 
ordem reversa à qual o construtor é executado. Quando um objeto da classe derivada é destruído, o 
destrutor da classe derivada é chamado e em seguida o destrutor da classe-base é chamado. 
Esta ordem não é mantida quando um ponteiro para a classe-base contém um objeto de uma 
classe derivada. Se o operador delete é aplicado a um ponteiro da classe-base, o compilador chama 
somente o destrutor da classe-base, mesmo que o ponteiro aponte para um objeto da classe 
derivada. 
A solução é declarar o destrutor da classe-base virtual. Isto faz com que os destrutores de 
todas as classes derivadas sejam virtuais, mesmo que não compartilhem o mesmo nome do destrutor 
da classe-base. Então, se delete é aplicado a um ponteiro para a classe-base, os destrutores 
apropriados são chamados, independentemente de qual tipo de objeto é apontado pelo ponteiro. 
class Base 
{ 
 public: virtual ~Base(); // destrutor virtual 
}; 
 
A classe Base tem um destrutor virtual, mesmo sendo um destrutor que não faz nada. 
Sempre que você escrever uma classe que tem uma função virtual, ela deve ter um destrutor virtual 
mesmo que não necessite dele. A razão disto é que uam classe derivada pode necessitar de um 
destrutor. Se um destrutor virtual está definido na classe-base, você assegura que destrutores de 
classes derivadas são chamados na ordem necessária. Observe que, enquanto os destrutores podem 
ser virtuais, os construtores não devem ser. 
17.3 Classe-Base Virtual 
Além de declarar funções virtuais, podemos usar a palavra virtual para declarar uma classe 
inteira. A necessidade de declarar uma classe virtual