Apostila C - C++
170 pág.

Apostila C - C++


DisciplinaIntrodução à Tecnologia da Computação26 materiais156 seguidores
Pré-visualização50 páginas
das notas : &quot; << Notas.Media(i); 
return 0; 
} 
O programa cria uma matriz fixa para armazenar as notas dos alunos, imprimindo a média 
das notas no final. A novidade é a função operator[] que checa se o índice especificado está 
dentro do limite de dimensionamento da matriz. Se estiver, a função retorna uma referência ao 
elemento correspondente da matriz privada. Se não estiver, a função imprime uma mensagem de 
erro e retorna uma referência a uma variável static float com valor \u20131. Isto previne a 
sobreposição de outras regiões da memória caso o limite da matriz seja ultrapassado. 
A função operator [] sobrecarrega o operador binário [] e está definida dentro da 
classe Matriz. Por isso, recebe um único parâmetro int i. A função é definida fora da classe, e 
por isso que escrevemos Matriz:: antes do nome operator [] para indicar que se trata de uma 
função membro. Observe que a função operator [] retorna uma referência a um elemento da 
matriz n. Dessa forma, a expressão notas[i] age como o próprio elemento da matriz privada, 
podendo ser usado em instruções como : 
cin >> notas[i]; 
notas[i] = 67.75; 
Retornar uma referência não é simplesmente eficiente, mas sim necessário. Caso contrário, 
não teremos acesso direto a um elemento da classe mas a uma cópia deste, não podendo assim 
alterar o valor deste elemento com instruções de atribuição como as declaradas acima. 
A função operator [] cria uma variável static, pois não podemos retorar uma referência 
para uma variável local (automática) já que esta será destruída ao final da execução da função. 
Aqui começamos a discutir alguns aspectos sobre o operador de atribuição (=). Você sabe 
que o operador = pode atribuir o valor de uma variável simples a outra do mesmo tipo. Podemos 
também atribuir um objeto a outro da mesma classe usando instruções como: 
Obj2 = Obj1; 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 114
 
Geralmente, quando o valor de um objeto é atribuído a outro de mesma classe, o valor de 
todos os seus membros de dados são simplesmente copiados no novo objeto. O compilador não 
executa nenhuma instrução especial no uso de atribuições entre tipos definidos pelo usuário. 
A complexidade surge quando a atribuição é entre variáveis de tipos diferentes. Podemos 
converter um valor float para uma variável inteira através de uma das seguintes intruções: 
int i = int(3.141567); 
int i = (int) 3.141567; 
Assim como podemos converter um tipo básico em outro tipo, podemos também converter 
um objeto de uma classe em um tipo básico ou em um objeto de outra classe. 
Para converter um tipo definido pelo programador, como classes, num tipo básico é 
necessário sobrecarregar o operador de molde, criando uma função chamada conversora. No 
exemplo anterior, sobrecarregamos o operador float *() como sendo uma função da classe 
Matriz, para converter um objeto desta classe em um ponteiro para uma lista de valores float: 
operator float *() const // Funcao Conversora 
{ return (float *)n; } 
Este operador foi chamado pela instrução: 
float * Temp = Notas; 
que poderia ter sido escrita na forma explícita: 
float * Temp = float *(Notas); 
As duas formas têm exatamente o mesmo efeito. Primeiro o compilador procura pela 
sobrecarga da operação de atribuição =() para este tipo, se não encontra, então o compilador 
procura pela função conversora para este tipo. Note que a função conversora não declara um tipo de 
retorno, o próprio nome da função já define o tipo de retorno. 
No próximo exemplo vamos adicionar um conjunto de operadores a classe Ponto definida 
anteriormente, para apresentar outras características e exemplos da sobrecarga de operadores. 
// Classe Ponto ... exemplo da sobrecarga de operadores 
#include <iostream> 
using namespace std; 
enum bool { false, true }; 
class Ponto 
{ 
public: 
int X, Y; 
Ponto(int aX = 0, int aY = 0) { X = aX; Y = aY; } 
Ponto operator ++(); // prefixado 
Ponto operator ++(int); // pos-fixado 
Ponto operator +(Ponto const aP) const; 
Ponto operator +=(int const aN); 
bool operator ==(Ponto const aP) const; 
Ponto operator =(int aN); 
}; 
Ponto Ponto::operator ++() //prefixado 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 115
{ 
++X; ++Y; 
return *this; 
} 
Ponto Ponto::operator ++(int) //pos-fixado 
{ 
++X; ++Y; 
return Ponto(X-1, Y-1); 
} 
Ponto Ponto::operator +(Ponto const aP) const 
{ 
return Ponto(X + aP.X, Y + aP.Y); 
} 
Ponto Ponto::operator +=(int const aN) 
{ 
X += aN; Y += aN; 
return *this; 
} 
bool Ponto::operator ==(Ponto const aP) const 
{ 
return ((X == aP.X)&&(Y == aP.Y)); 
} 
Ponto Ponto::operator =(int aN) 
{ 
X = aN; 
return *this; 
} 
class PFloat 
{ 
public: 
float X, Y; 
PFloat(float aX = 0, float aY = 0) 
{ X = aX; Y = aY; } 
operator Ponto() const // Converte tipo 
{ 
return Ponto((int) X, (int) Y); 
} 
}; 
// Operadores para objetos Iostream 
ostream & operator<<(ostream & OS, Ponto const aP) 
{ 
OS << '(' << aP.X << ','<< aP.Y <<')'; 
} 
int main(int argc, char* argv[]) 
{ 
Ponto P1, P2(2,3), P3; 
P3 = 2; 
PFloat Pf( 2.12, 3.14); 
P1 = Pf; 
cout << &quot;\n p1 = &quot; << P1; 
Curso de Linguagem Computacional C/C++
 
________________________________________________________________________________________________ 
Sistemas Industriais Inteligentes \u2013 DAS \u2013 CTC \u2013 UFSC 116
 
cout << &quot;\n ++p1 = &quot; << ++P1; 
cout << &quot;\n p2 == p1 ? -> &quot; << (P1 == P2); 
cout << &quot;\n p2 = &quot; << P2; 
cout << &quot;\n p2 + p1 = &quot; << (P2+P1); 
cout << &quot;\n pf = &quot; << Pf; 
cout << &quot;\n p3 = &quot; << P3; 
return 0; 
} 
Começamos definindo um tipo enum, para representar os valores booleanos. 
A classe Ponto possui agora os atributos X e Y como sendo dados públicos, somente para 
facilitar o acesso a estas variáveis. Ponto possui também um construtor default para a inicialização 
dos dados. 
Os primeiros operados sobrecarregados no exemplo são os operadores unários de 
incremento pré-fixado e pós-fixado. Dentre os operadores unários existentes em C++, a sobrecarga 
dos operadores de incremento e decremento apresenta uma dificuldade maior pelo fato de eles 
operarem de forma diferente quando prefixados ou pós-fixados. 
A declaração : 
Ponto operator ++(); // prefixado 
notifica o compilador da sobrecarga do operador prefixado. A definição do operador é 
idêntica à definição apresentada anteriormente. A primeira diferença é que agora o operador está 
definido fora da definição da classe. A segunda diferença está na forma como retornamos o 
resultado da operação. Podemos fazê-la de três formas: 
a) Ponto Temp( X, Y); 
return Temp; 
b) return Temp( X, Y); 
c) return *this; 
As duas primeiras formas criam um objeto temporário, armazenam nele os valores obtidos 
na operação e retornam este objeto temporário. Neste caso, não podemos utilizar a passagem de 
parâmetros por referência pois estes objetos temporários serão destruídos ao término da função. 
O terceiro utiliza o ponterio especial this. O ponteiro this é um ponteiro especial que 
pode ser acessado por todas as funções-membro do objeto. Este ponteiro aponta para o próprio 
objeto. Qualquer função-membro pode acessar o endereço do objeto do qual é membro por meio do 
ponteiro this. Quando um objeto é criado, o compilador atribui o endereço do objeto ao ponteiro 
this. Usamos o ponteiro this quando queremos que uma função-membro retorne o próprio 
objeto do qual é membro. Para retornar o próprio objeto, devemos utilizar *this. 
Após o operador de incremento pré-fixado, definimos o operador de incremento pós-fixado. 
Mas como pode o compilador distinguir os dois, sendo que ambos utilizam o mesmo simbolo e 
possuem o mesmo operando? Neste caso, C++