





















Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Apostila c
Tipologia: Notas de estudo
1 / 29
Esta página não é visível na pré-visualização
Não perca as partes importantes!






















Uma classe é um tipo definido pelo programador que contém o molde, a especificação para os objectos, tal como o tipo inteiro contém o molde para as variáveis declaradas como inteiros. A classe envolve e/ou associa, funções e dados, controlando o acesso a estes, definí-la implica especificar os seus atributos ( dados ) e suas funções membro ( código ).
ex:. Um programa que utiliza uma interface controladora de um motor eléctrico definiria a classe motor. Os atributos desta classe seriam: temperatura, velocidade, tensão aplicada. Estes seriam representados na classe por tipos como float ou long. As funções membro desta classe seriam funções para alterar a velocidade, ler a temperatura, etc.
Vamos supor um programa que controla um motor eléctrico através de uma saída serie. A velocidade do motor é proporcional à tensão aplicada, e esta proporcional aos bits que vão para saída serie que passam por um conversor digital analógico ( DAC ).
Vamo - nos abstrair de todos estes detalhes por enquanto e modelar somente a interface do motor como uma classe, a pergunta é que funções e que dados membro deve ter nossa classe, e que argumentos e valores de retorno devem ter essas funções membro:
A velocidade do motor será representada por um atributo, inteiro (int). Usaremos o número de bits que precisarmos, caso o valor de bits necessário não possa ser fornecido pelo tipo , usaremos então o tipo long , isto depende do conversor digital analógico utilizado e do compilador.
O motor precisa conhecer a saída serie, a sua ligação com o "motor do mundo real". Vamos supor uma representação em hexadecimal do atributo endereço de porta serie, um possível nome para o atributo: enderecomotor.
Internamente o utilizador da classe motor pode desejar alterar a velocidade, cria-se então a função : void altera_velocidade(int novav); O valor de retorno da função é void ( valor vazio ), poderia ser criado um valor de retorno (int) que indicasse se o valor de velocidade era permitido e foi alterado ou não era permitido e portanto não foi alterado.
O C++ permite que se acrescente funções de manipulação da struct na declaração, juntando tudo numa só entidade que é uma classe. Essas funções membro podem ter sua declaração ( cabeçalho ) e implementação ( código ) dentro da struct ou só o cabeçalho ( assinatura ) na struct e a implementação, código, fora. Este exemplo apresenta a primeira versão, o próximo a segunda versão ( implementação fora da classe ). Essas funções compõem a interface da classe. A terminologia usada para designá-las é bastante variada: funções membro, métodos, etc. Quando uma função membro é chamada, diz - se que o objecto está a receber uma mensagem ( para executar uma acção ). Um programa simples para testes sobre funções membro seria o seguinte:
Exemplo 1 : #include <iostream.h
struct contador //conta ocorrencias de algo { int num; //numero do contador
void incrementa(void){num=num+1;}; //incrementa contador
void comeca(void){num=0;}; //comeca a contar };
void main() //teste do contador { contador umcontador;
float raio; float x; //atributo coordenada cartesiana x float y; //atributo coordenada cartesiana y
void move(float dx,float dy) //função membro ou função membro move { x+=dx; //equivale a x=x+dx; y+=dy; }
void mostra(void) //função membro ou função membro mostra { cout << "Raio:"<<raio <<endl; cout << "X:"<<x << endl; cout << "Y:" <<y<< endl; } };
void main() { circulo ac; // * instanciação de um objecto circulo (criação) ac.x=0.0; ac.y=0.0; ac.raio=10.0; ac.mostra(); ac.move(1.0,1.0); ac.mostra(); ac.x=100.0; ac.mostra(); }
Resultado do programa: Raio: X: Y: Raio: X: Y:
Raio: X: Y:
Comentários: A função membro move altera as coordenadas do objecto. O objecto tem as coordenadas x e y somadas com os argumentos dessa função membro. Nota que esta função membro representa uma maneira mais segura, clara, elegante de alterar as coordenadas do objecto do que acessá-las directamente da seguinte forma: ac.x+=dx;. ac.y+=dy;. Lembre-se que ac.x+=dx é uma abreviação para ac.x=ac.x+dx;.
Como funcionam no compilador as chamadas de funções membro: É possível imaginar que as definições de funções membro ocupam um grande espaço na representação interna dos objectos, mas lembre-se que elas são todas iguais para uma classe, então basta manter para cada classe uma tabela de funções membro que é consultada no momento da chamada. Os objectos só precisam ter uma referência para esta tabela.
Até agora só tínhamos visto funções membro com valor de retorno igual a void. Uma função membro, assim como uma função comum, pode retornar qualquer tipo, inclusive os definidos pelo programador. Sendo assim, sua chamada no programa aplica - se a qualquer lugar onde se espera um tipo igual ou equivalente ao tipo do seu valor de retorno, seja numa lista de argumentos de outra função , numa atribuição ou num operador como o cout << variavel;
Exemplo 3 : #include <iostream.h
struct contador //conta ocorrencias de algo
int num; //numero, posicao do contador
void incrementa(void){num=num+1;}; //incrementa contador void comeca(void){num=0;}; //comeca a contar, "reset" int retorna_num(void) {return num;};
struct teste { int x;
void altera_x(int v); //somente definicao implementacao vem depois, fora da classe };
void teste::altera_x(int v) { x=v;} //esta já é a implementacao codigo
void main() { teste a; //instaciação de um objecto
a.altera_x(10); //chamada da função membro com valor 10 que sera impresso a seguir
cout << a.x; //imprime o dado membro }
Resultado do programa anterior: 10
Exemplo 5 : Programa exemplo círculo, mais complexo:
#include <iostream.h
//para cout
struct circulo { float raio; float x; float y;
void inicializa(float ax,float by,float cr); void altera_raio(float a); float retorna_raio(void);
void move(float dx,float dy); void mostra(void); };
void circulo::inicializa(float ax,float by,float cr) { x=ax; y=by; raio=cr; }
void circulo::altera_raio(float a) { raio=a; }
float circulo::retorna_raio(void) { return raio; }
void circulo::move(float dx,float dy) { x+=dx; y+=dy; }
void circulo::mostra(void) { cout << "Raio:"<< retorna_raio() <<endl; cout << "X:"<<x << endl; cout << "Y:" <<y<< endl; }
void main() { circulo ac;
ac.inicializa(0.0,0.0,10.0);
float circulo::retorna_raio(void) { return raio; //tenho acesso direto a raio. }
Segurança: Em C++ o programador pode aceder directamente os dados do tipo definido pelo usuário: ac.x=100.0.
Veremos maneiras de proibir em C++ este tipo de acesso directo ao dado membro, deixando este ser modificado somente pelas funções membro. Isto nos garante maior segurança e liberdade pois podemos permitir ou não o acesso para cada dado membro de acordo com nossa vontade.
Eficiência: Alguém pode argumentar que programas que usam bastante chamadas de funções podem se tornar pouco eficientes e que poderia ser melhor aceder diretamente os dados de um tipo definido pelo usuário ao envés de passar por todo o trabalho de cópia de argumentos, inserção da função no pilha, etc.
Na verdade não se perde muito em eficiência, e além disso muitas vezes não se deseja permitir sempre o acesso directo aos dados de um tipo definido pelo programador por razões de segurança. Nesse sentido o C++ oferece um recurso que permite ganhos em segurança sem perder muito em eficiência.
Construtores são funções membro especiais chamadas pelo sistema no momento da criação de um objecto. Elas não possuem valor de retorno, porque você não pode chamar um construtor para um objecto. Construtores representam uma oportunidade de inicializar de forma organizada os objectos; imagina se tu te esqueces de inicializar correctamente ou o fazes duas vezes, etc.
Um construtor tem sempre o mesmo nome da classe e não pode ser chamado pelo utilizador desta. Para uma classe string o construtor teria a forma string(char* a); com o argumento char* especificado pelo programador. Ele seria chamado automaticamente no momento da criação, declaração de uma string:
string a("Texto"); //alocacao estatica implica na chamada do construtor
a.mostra(); //chamada de metodos estatica.
Existem variações sobre o tema que veremos mais tarde: Sobrecarga de construtor, "copy constructor", como conseguir construtores virtuais ( avançado, não apresentado neste texto ), construtor de corpo vazio.
O exemplo seguinte é simples, semelhante aos anteriores, presta atenção na função membro com o mesmo nome que a classe ( struct ), este é o construtor:
Exemplo 6 : #include <iostream.h
struct ponto { float x; float y;
public:
ponto(float a,float b); //esse e o contrutor, note a ausencia do valor de retorno void mostra(void); void move(float dx,float dy); };
ponto::ponto(float a,float b) //construtor tem sempre o nome da classe. { x=a; //incializando atributos da classe y=b; //colocando a casa em ordem }
void ponto::mostra(void) {cout << "X:" << x << " , Y:" << y << endl;}
void ponto::move(float dx,float dy)
p1(x1,y1) e p2(x2,y2) são as chamadas dos constructores da classe ponto, elas devem ficar fora do corpo {} do constructor, nesta lista separada por vírgulas deves inicializar todos os atributos. Os tipos básicos como int, float, etc podem ser inicializados nessa lista.
Por exemplo se a classe recta tivesse um atributo inteiro de nome identificação, a lista poderia ser da seguinte forma:
reta(float x1,float y1,float x2,float y2):p1(x1,y1),p2(x2,y2),identificacao(10) { //nada mais a fazer, os construtores de p1 e p2 ja foram chamados }
seria como se identificação tivesse um constructor que tem como argumento o seu valor. ou
recta(float x1,float y1,float x2,float y2):p1(x1,y1),p2(x2,y2) { identificacao=10; //tambem pode, porque tipos básicos (int) em C++ não são objectos // portanto nao tem construtores }
Vamos ao exemplo, que novamente é semelhante aos anteriores, para que o leitor preste atenção somente nas mudanças, que são os conceitos novos, sem ter que se esforçar muito para entender o programa:
Exemplo 7 : #include <iostream.h
struct ponto { float x; float y; //coordenadas
ponto(float a,float b) { x=a; y=b;
} //construtor
void move(float dx,float dy) { x+=dx; y+=dy; } //funcao membro comum
void inicializa(float a,float b) { x=a; y=b; }
void mostra(void) {cout << "X:" << x << " , Y:" << y << endl;} };
struct recta { ponto p1; ponto p2;
recta(float x1,float y1,float x2,float y2):p1(x1,y1),p2(x2,y2) { //nada mais a fazer, os contrutores de p1 e p2 ja foram chamados }
void mostra(void); };
void recta::mostra(void) { p1.mostra(); p2.mostra(); }
void main() { recta r1(1.0,1.0,10.0,10.0); //instanciação da recta r
r1.mostra(); }
Resultado do programa:
//funcao membro comum, pode ser chamada pelo usuario
~contador(void) {cout << "Contador destruido, valor:" << num <<endl;} //destrutor };
void main() { contador minutos(0);
minutos.incrementa(); cout << minutos.num << endl;
//inicio de novo bloco de codigo contador segundos(10);
segundos.incrementa(); cout << segundos.num <<endl; //fim de novo bloco de codigo }
minutos.incrementa(); }
Resultado do programa: 1 11 Contador destruido, valor: Contador destruido, valor:
Comentários: No escopo de main é criado o contador minutos com valor inicial==0. Minutos é incrementado, agora minutos.num==1. O valor de num em minutos é impresso na tela. Um novo bloco de código é criado. Segundos é criado, instanciado como uma variável deste bloco de código, o valor inicial de segundos é 10, para não confundir com o objecto já criado. Segundos é incrementado atingindo o valor 11. O valor de segundos é impresso na tela. Finalizamos o bloco de código em que foi criado segundos, agora ele sai de escopo, é apagado, mas antes o sistema chama
automaticamente o destructor. Voltando ao bloco de código de main(), minutos é novamente incrementado. Finalizamos main(), agora, todas as variáveis declaradas em main() saem de escopo, mas antes o sistema chama os destructores daquelas que os possuem.
Encapsulamento, "data hiding". Neste tópico vamos falar das maneiras de restringir o acesso as declarações de uma classe, isto é feito em C++ através do uso das palavras reservadas public, private e protected. Friends também restringe o acesso a uma classe.
Não apresentaremos mais exemplos de classes declaradas com struct. Tudo que foi feito até agora pode ser feito com a palavra class ao envés de struct, incluindo pequenas modificações. Mas porque usar só class nesse tutorial? A diferença é que os dados membro e funções membro de uma struct são acessíveis por "default" fora da struct enquanto que os atributos e métodos de uma classe não são, acessíveis fora dela (main) por "default".
Então como controlar o acesso de atributos e métodos numa classe? Simples, através das palavras reservadas private, public e protected.
Protected está relacionada com herança, por agora vamos focalizar a nossa atenção em private e public que qualificam os dados membro e funções membro de uma classe quanto ao tipo de acesso ( onde eles são visíveis ). Public, private e protected podem ser vistos como qualificadores, "specifiers".
Para facilitar a explicação suponha a seguintes declarações equivalentes de classes:
class ponto { float x; //dados membro float y;
public: //qualificador
void inicializa(float a, float b) {x=a; y=b;}; //funcao membro
Fica fácil entender essas declarações se pensares no seguinte: esses qualificadores aplicam - se aos métodos e atributos que vem após eles, se houver então um outro qualificador, teremos agora um novo tipo de acesso para os métodos declarados posteriormente.
Mas então porque as declarações são equivalentes? É porque o qualificador private é "default" para class, ou seja não especificares nada , até que se insira um qualificador, tudo o que for declarado numa classe é private. Já em struct, o que é default é o qualificador public.
Agora vamos entender o que é private e o que é public:
Vamos supôr que instanciaste ( criaste ) um objecto do tipo ponto em seu programa:
ponto meu; //instanciação
Segundo o uso de qualquer uma das definições da classe ponto dadas acima não podes escrever no teu programa:
meu.x=5.0; //erro!
,como fazias antes, a não ser que x fosse declarado depois de public na definição da classe o que não ocorre aqui. Mas podes escrever x=5.0; na implementação ( dentro ) de um método porque enquanto não for feito uso de herança, porque uma função membro tem acesso a tudo o que é de sua classe, veja o programa seguinte.
Você pode escrever: meu.move(5.0,5.0); ,porque a declaração ( move ) está na parte public da classe. Aqui o leitor já percebe que podem existir funções membro private também, e estas só são acessíveis dentro do código da classe (outras funções membro).
Aplicar encapsulamento a classe ponto definida anteriormente.
Exemplo 9 : #include <iostream.h
class ponto { private: //nao precisaria por private, em class e default
float x; //sao ocultos por default float y; //sao ocultos por default
public: //daqui em diante tudo e acessivel.
void inicializa(float a,float b) { x=a; y=b; } //as funcoes de uma classe podem acessar os atributos private dela mesma.
void mostra(void) {cout << "X:" << x << " , Y:" << y << endl;} };
void main() { ponto ap; //instanciação
ap.inicializa(0.0,0.0); //métodos public ap.mostra(); //metodos public }
Resultado do programa: X:0 , Y:
Comentários: Este programa não deixa tirar o ponto de (0,0) a não ser que seja chamada inicializa novamente. Fica claro que agora, encapsulando x e y precisamos de mais métodos para que a classe não tenha a sua funcionalidade limitada.
Novamente: escrever ap.x=10; em main é um erro! Pois x está qualificada como private.
Este programa é uma variante do anterior, a única diferença é que Y é colocado na parte public da definição da classe e é acessado directamente.