Apostila UFSC - Linguagem C
170 pág.

Apostila UFSC - Linguagem C


DisciplinaLinguagem de Programação C285 materiais933 seguidores
Pré-visualização50 páginas
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 é quando esta foi usada como classe-base para 
mais de uma clase derivada e dessas classes derivadas foi derivada outra por meio de herança 
múltipla. Uma classe-base não pode ser usada mais de uma vez numa mesma classe derivada. 
Entretanto, uma classe-base pode ser indiretamente usada mais de uma vez em classes derivadas. 
Observe a classe Super do exemplo abaixo: 
#include <iostream> 
using namespace std; 
class Base { 
protected: int valor; 
public: Base(int n = 0) {valor = n;} 
virtual void Print() { cout << &quot;\nValor : &quot; << 
valor; } 
}; 
class Deriv1 : virtual public Base { 
public: Deriv1(int n = 0) : Base(n) { } 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 135
}; 
class Deriv2 : virtual public Base { 
public: Deriv2(int n = 0) : Base(n) { } 
}; 
class Super : public Deriv1, public Deriv2 
{ 
public: 
Super(int n = 0) : Base(n) { } 
int RetValor() { return valor; } 
void Print() { cout << &quot;\nValor do Super-Objeto : &quot; << valor; } 
}; 
int main(int argc, char* argv[]) 
{ 
Base B(5); 
Deriv1 D1(10); 
Super S(343); 
B.Print(); 
D1.Print(); 
S.Print(); 
return 0; 
} 
Neste caso, cada objeto da class Super poderia ter dois sub-objetos da classe Base. Se um 
objeto da classe Super tentasse acessar dados ou funções da classe Base, ocorreria um erro de 
ambiguidade. A declaração virtual nas classes Deriv1 e Deriv2 faz com que estas classes 
compartilhem uma única classe-base. A classe Super só terá um subobjeto da classe Base e não terá 
mais nenhum problema de ambigüidade. 
Os construtores de classes-base são sempre executados antes do construtor da classe 
derivada. Os construtores de classes-base virtuais são chamados antes de qualquer construtor de 
classes-bases não-virtuais. 
17.4 Funções Amigas 
O conceito de isolamento interno dos itens de uma classe, onde funções não-membros não 
teriam o privilégio de acesso aos dados privados ou protegidos desta classe, é violado por um 
mecanismo oferecido por C++ que permite que funções não-membro, às quais foi dada uma 
permissão especial, tenham acesso à parte interna da classe. Declarando uma função como amiga, 
ela tem os mesmos privilégios de uma função-membro, mas não está associada a um objeto da 
classe. No programa abaixo apresentamos a sintaxe das funções amigas: 
// Uso de funcoes amigas 
#include <iostream> 
#include <strstrea> 
using namespace std; 
class Data; // Declara que existe esta classe 
class Tempo 
{ 
private: 
long h, min, s; 
public: 
Tempo(long hh = 0, long mm = 0, long ss = 0) 
{ h = hh; min = mm; s = ss; } 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 136
 
friend char * PrintTime( Tempo &, Data &); 
}; 
class Data 
{ 
private: 
int d, m, a; 
public: 
Data(int dd = 0, int mm = 0, int aa = 0) 
{ d = dd; m = mm; a = aa; } 
friend char * PrintTime( Tempo &, Data &); 
}; 
char * PrintTime( Tempo & Tm, Data & Dt) // Função global e amiga 
{ 
char * temp = new char[50]; 
 memset(temp, '\0', 50); 
strstream sIO(temp, 50, ios::out); 
sIO << &quot;\n Relogio-> \t&quot; << Tm.h << &quot;:&quot; << Tm.min << &quot;:&quot; << Tm.s; 
sIO << &quot;\n Data-> \t&quot; << Dt.d << &quot;/&quot; << Dt.m << &quot;/&quot; << Dt.a; 
return temp; 
} 
int main(int argc, char* argv[]) 
{ 
Tempo Tm( 15, 56, 4); 
Data Dt( 9, 5, 2000); 
char * str = PrintTime( Tm, Dt); 
cout << &quot;\n&quot; << str; 
delete str; 
return 0; 
} 
Neste exemplo, queremos que a função PrintTime() tenha acesso aos dados privados da 
classe Tempo e Data. Desta forma, usamos a palavra-chave friend na declaração da função 
friend char * PrintTime( Tempo &, Data &); 
Esta declaração pode ser colocada em qualquer posição da classe. Não há diferença se for 
colocada na parte pública ou na parte privada da classe. Um objeto Tempo é passado como 
argumento à função PrintTime(). Este argumento é necessário pois, mesmo quando se dá a uma 
função amiga o privilégio de acesso aos dados privados da classe, ela não é parte daquela classe, 
devendo ser informada sobre qual objeto agirá. 
A função PrintTime() cria um objeto da classe strstream. Este objeto atua como se 
fosse um objeto cout, entretanto em vez de enviar os dados para a saída padrão (tela), ele 
armazena estes dados no buffer de caracteres fornecidos ao seu construtor. Esta é uma das formas 
mais fáceis para converter dados de uma classe para texto (ASCII) e armazenar estes dados em um 
buffer de caracteres. Note que operamos sob o objeto criado assim como operamos com o objeto 
cout. O mesmo princípio pode ser empregado para a leitura de dados armazenados em um buffer 
de caracteres, operando-se como se estivéssemo utilizando o operador cin. 
Funções amigas são muito empregadas quando sobrecarregamos os operadores << e >> 
associados aos objetos cout e cin respectivamente. Desta forma definimos o operador << para 
imprimir os dados da classe e o operador >> para adquirir os dados da classe. Como, muitas vezes 
desejamos que estes operadores tenham acesso aos membros privados da classe, então devemos 
defini-los como funções amigas. 
friend ostream & operator <<(ostream & OS, Tempo const & T); 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 137
friend istream & operator >>(istream & IS, Tempo & T; 
Todo e qualquer operador que queremos sobrecarregar como sendo uma função global (i.e., 
recebe um parâmetro se for unário e dois se for binário) e queremos permitir que este operador 
tenha acesso a membros privados e protegidos de uma dada classe, devemos definir este operador 
como sendo uma função amiga. 
No nosso programa exemplo acima, definimos que a função PrintTime()é amiga das 
classes Data e Tempo. Para tal, precisamos declarar nas duas classes que esta função é amiga da 
classe. Quando declaramos a função na classe Tempo, estaremos declarando uma função que 
recebe como parâmetro um objeto da classe Data desconhecida até então pelo compilador. Para 
evitar que o compilador gere um erro indicando que a classe não existe, devemos declarar no início 
do arquivo que existe a classe Data, e que esta será definida posteriormente. Fazemos isto através 
da instrução: 
class Data; 
17.5 Classes Amigas 
Além de ser possível