Módulo 11 – Programação Orientada à Objetos
Nos módulos anteriores, cobrimos todos os aspectos essenciais da programação estruturada em C++. Entretanto, a linguagem C++ possui muito mais aspectos do que os tratados até agora. Pode-se dizer que tocamos apenas na ponta do iceberg: o verdadeiro poder de C++ está na sua versatilidade e capacidade de ser programada em vários paradigmas diferentes, seja programação estruturada, orientada à objetos ou orientada à templates. O módulo à seguir tenta fazer uma explicação básica sobre o que é a programação orientada à objetos, dando aos leitores uma breve introdução aos conceitos essenciais deste paradigma, sem adentrar em exemplos de código.
11.1 – Paradigmas de Programação
Como dissemos no primeiro módulo desta apostila, paradigmas de programação são conjuntos de idéias que fornecem ao programador uma visão sobre a estruturação e execução de um programa. Assim como ao resolver um problema podemos adotar uma entre variadas metodologias para resolvê-lo, ao criar um programa podemos adotar um determinado paradigma de programação para desenvolvê-lo.
O primeiro paradigma que aprendemos quando começamos à estudar programação, qualquer que seja a linguagem, é o paradigma da programação estruturada. A programação estruturada tem como objetivo escrever programas que sigam uma lógica linear, ou seja, tenham começo, meio e fim. À grosso modo, um programa escrito dessa forma começa com a declaração das variáveis que serão utilizadas, seguindo para a execução de comandos, funções e tomadas de decisão numa sequência linear, até que todas as linhas de código tenham sido executadas e o programa atinja seu final. É a maneira mais simples de se escrever um programa, principalmente porque utilizamos essa mesma lógica linear no dia-a-dia: faça tal tarefa, depois caso tal condição seja verdadeira realize outra tarefa, e assim por diante. Porém, quando precisamos escrever programas realmente grandes ou realmente complexos, a programação estruturada torna-se ineficiente. Imagine um editor de textos, onde se pode escrever em um documento, criar um novo documento, alterar fontes, tamanhos, parágrafos, tabulações, executar correções ortográficas...agora imagine como acomodar todas as diferentes ações que um usuário pode executar em um programa de estrutura linear.
11.1 – Programação Orientada à Objetos
O paradigma da programação orientada à objetos nasceu no começo da década de 1970, e tinha por objetivo propor uma nova maneira de se olhar para a elaboração de programas. Para a programação orientada à objetos, um programa deve ser visto como uma coleção de objetos que trabalham cooperando entre si, ao contrário de uma lista de instruções que deve ser seguida pelo computador. Desse modo, cada objeto dentro de um programa deve ser visto como uma “máquina” independente com um determinado papel ou objetivo na execução do programa. Estes objetos devem se comunicar entre si, recebendo, processando e enviando dados para os outros objetos do programa.
11.2 – Conceitos Básicos
A unidade principal da programação orientada à objetos é a Classe. Uma classe é uma representação de um objeto ou idéia real em forma de código, separando as características operacionais dos detalhes concretos de sua implementação. Simplificando, definimos uma Classe para representar algo: por exemplo, um relógio. Para o usuário, não é importante como o relógio funciona, tanto na vida real como na programação: é importante apenas que o usuário possa interajir com o relógio, seja olhando as horas, seja ajustando o alarme ou configurando o horário correto. As engrenagens ou funções que fazem o relógio funcionar ficam “escondidas” do usuário: é isso que queremos dizer com “separar as características operacionais dos detalhes da implementação”.
Em termos de programação, uma classe é uma estrutura que contém variáveis e funções com o objetivo de representar uma idéia. Dentro de uma classe, as variáveis e funções são chamadas de “membros da classe”. Ainda pensando no exemplo do relógio, dentro desta classe teríamos variáveis (ou membros) para as horas, minutos, segundos e também para armazenar o horário do alarme, assim como funções-membro para ver o horário atual, ajustá-lo ou ajustar o alarme. Porém, o usuário não precisa ter acesso à todas estas variáveis: eles só precisa interajir com as características operacionais do relógio. Assim, criamos funções-membro para fazer esta interação: uma função para ver as horas, outra para ajustar o horário e outra para ajustar o alarme. Dessa forma, simplificamos a utilização da classe e também evitamos que o usuário acesse e altere dados que possam atrapalhar o funcionamento do programa.
Dizemos que os membros da classe que o usuário pode acessar são “públicos”, enquanto que os membros internos são “privados”. Um membro privado não pode ser acessado ou alterado pelo usuário: não queremos o usuário “cutucando” as engrenagens do relógio. Os membros privados são acessados somente pelos membros públicos, que fazem a interação entre as “engrenagens” do programa e o usuário. Assim, mesmo que tenhamos funções como membros privados, elas só podem ser executadas por outras funções que sejam membros públicos da função. A função pública “ver horário” de nossa classe Relógio provavelmente precisa acessar uma função privada que atualiza o horário do relógio, assim como a função “ajusta alarme” altera uma variável privada que contém o horário do alarme.
Formalizando, um membro público, seja ele uma variável ou uma função pode ser acessado a qualquer momento durante a execução de um programa (lido ou alterado no caso das variáveis, executado no caso das funções). Quando tentamos acessar diretamente um membro privado, geramos um erro pois seu acesso é proibido para o usuário/programador! Assim, a única forma de acessar um membro privado é através dos membros públicos. Esta separação entre membros públicos e privados é feita na declaração da classe.
O próximo conceito essencial da programação orientada à objetos é o próprio objeto. Um objeto é uma instância específica de uma classe. Podemos pensar na classe como um tipo de variável, enquanto que o objeto é a própria variável. Em C++, definimos classes para depois criar objetos que tenham as características definidas nestas classes. No exemplo acima, definimos a classe Relógio, e agora podemos criar quantos relógios forem necessários: cada relógio será um objeto diferente, com funcionamento idêntico e características diferentes. Por exemplo, criamos os objetos “relógio de pulso” e “relógio-cuco”. Os dois são relógios, mas podemos ajustá-los em horários diferentes, com alarmes diferentes. Cada objeto possui todos os membros públicos e privados que definimos na declaração da classe. Assim, utilizamos os membros públicos para interajir com os objetos, enquanto que os membros privados são as “engrenagens” do objeto. Aqui temos um novo nome para os membros públicos: chamamos eles de “métodos” do objeto. Um método nada mais é do que uma função que interaja com o objeto, ou seja, são próprios os membros públicos que definimos na declaração da classe.
Para termos um exemplo mais concreto, podemos pensar em um programa qualquer do ambiente Windows. Nas ferramentas de programação Windows, temos várias classes que representam os vários itens que vemos na tela: janelas, menus, cursores, ícones. Quando um programador precisa criar uma janela, ele não precisa ter todo o trabalho para programar uma janela do zero: ele cria um novo objeto da classe Janela, e define suas características através das funções públicas (ou “métodos”) da classe. Ele não precisa saber como a janela é desenhada na tela: basta saber quais métodos definem as características que ele necessita para que seu objeto funcione da maneira desejada.
11.2 – Herança e Polimorfismo
O conceito de herança é muito importante para a reutilização de código em C++. Quando escrevemos uma classe, assim como quando escrevíamos funções, temos em mente o objetivo de não reescrever um código que já foi desenvolvido. Além das classes, temos em C++ o conceito de sub-classes: classes que herdam seus membros e métodos de outras classes. Por exemplo, suponha que tenhamos criado a classe Cachorro. Com base nesta classe, podemos criar subclasses representando as diversas raças de cachorro, como Pastor_Alemão, Doberman e Vira_Lata. Não precisamos reescrever todos os membros e métodos que já havíamos escrito ao desenvolver a classe base: basta indicar ao compilador que as novas subclasses serão herdeiras da classe-base. Feito isso, podemos modificar estas subclasses, modificando seus membros públicos e privados. Assim, podemos alterar a interface da subclasse, afetando a maneira do usuário interajir com ela, ou o próprio funcionamento da classe, alterando seus membros privados.
Note que poderíamos simplesmente copiar a classe-base e modificar seu código, alterando seu nome. Mas o conceito de herança permite alterar só os membros que necessitam ser alterados. Não precisamos nem mesmo ter acesso ao código fonte da classe-base, o que é muito útil quando trabalhamos com bibliotecas comerciais que geralmente não disponibilizam seu códigos.
Quando alteramos uma subclasse, podemos alterar qualquer um de seus métodos, ou seja, podemos alterar a maneira como esta subclasse interaje com seus usuários. Por exemplo, quando definimos as subclasses Pastor_Alemão e Vira_Lata, alteramos também seu método “Latido”. Uma subclasse fará uma determinada tarefa quando for chamado o método “Latido”, enquanto que a outra subclasse fará outra tarefa totalmente diferente, mesmo sendo as duas subclasses derivadas da mesma classe-base. Podemos até criar uma subclasse sem o método “Latido”: um cachorro mudo. Essa possibilidade de se fazer alterações nas subclasses de uma classe base é chamada de Polimorfismo. Basicamente, o Polimorfismo afirma que mesmo que a classe B seja herdeira da classe A, ela não precisa herdar todos os métodos de A: a classe B pode ter características diferentes, ou seja, métodos diferentes da classe-base. Dessa forma, o mesmo “comando” pode gerar resultados diferentes dependendo da subclasse à qual o objeto pertence.
A idéia por trás dos conceitos de classes, objetos, herança e polimorfismo é possibilitar a elaboração de código mais simples para programas mais complexos, quebrando os diversos aspectos de um programa em blocos independentes entre si. Assim, não precisamos atacar todo o problema de uma vez, podemos dividí-lo e trabalhar com cada uma dessas divisões individualmente. Outro ponto importante por trás da programação orientada à objetos é a reutilização de código: porque reinventar a roda sempre que escrevemos um programa novo? Nesse aspecto, podemos pensar nas classes como grupos de funções: não precisamos saber como elas funcionam, apenas precisamos saber como utilizar seus métodos para obter os resultados desejados. Atualmente existe um número muito grande de bibliotecas de classes para C++, tanto comercial como em código aberto, com as aplicações mais diversas, desde bibliotecas matemáticas até bibliotecas gráficas para a elaboração de jogos 3D.
-------------------------------------------------------------------------------------------------------------
Publicado originalmente em - sexta-feira, 13 de agosto de 2010
atualizacao da referencia bibliografica em -atualizado em 21/08/2017 - 03:01
eu publiquei em uma epoca de ensino medio, sem entender de direito
autroal, o arquivo é do autor (Esta apostila é o resultado do trabalho
de conclusão de curso realizado por Enrique Camargo Trevelin, aluno
do curso de Engenharia Elétrica, sob a orientação do Prof. Carlos
Antônio Alves. ), está publicado no site :
(http://apostilacpp.awardspace.com/index.php?pagina=home) na integra e
peco desculpa se o autor quer que retire do site o material, mande um email para
contatomorgao@gmail.com