Ir para conteúdo
Faça parte da equipe! (2024) ×
Conheça nossa Beta Zone! Novas áreas a caminho! ×
  • Quem está por aqui   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.

Intrudução & Vocabulario C/C++


HoruS*
 Compartilhar

Posts Recomendados

Introdução ao C/C++

 

c1.jpg

 

Para muitos a transição de C para C++ não é fácil. De fato, essa transição é freqüentemente acompanhada de muita ansiedade porque C++ é popularmente envolto em uma aura de inacessibilidade. Por exemplo, você pode pegar um livro sobre C++, abri-lo em uma página qualquer e deparar-se com um parágrafo mais ou menos assim:

 

Do ponto de vista de projeto, derivação privada é equivalente a compartimentação, exceto pela, ocasionalmente importante, questão de sobreposição. Um uso importante disso é a técnica de derivar uma classe publicamente a partir de uma classe base abstrata, definindo uma interface, e privativamente de uma classe concreta provendo uma implementação. Porque a herança implícita na derivação privada é um detalhe de implementação que não é refletido no tipo da classe derivada, ela é algumas vezes denominada “herança de implementação” e contrasta com a declaração pública, onde a interface da classe base é herdada e a conversão implícita para o tipo da classe é permitida. O restante é algumas vezes referido como uma a sub-tipificação ou “herança de interface”. (Trecho extraído de “The C++ Programming Language, second edition, by Bjarne Stroustrup, page 413)

 

É realmente difícil iniciar-se em C++ com uma literatura assim tão rebuscada, tão hermética.

 

Essa série de tutoriais respondem a três questões bastante comuns:

 

Porque C++ existe e quais são suas vantagens sobre o C?

* Que recursos estão disponíveis no C++ para materializar idéias orientadas a objeto?

* Como você projeta e implementa código usando os princípios da orientação a objeto?

 

Uma vez que você tenha compreendido os recursos básicos disponíveis no C++, e saiba como e porque usá-los, você se tornará um programador em C++. Essas série de tutoriais vão iniciá-lo nessa direção, e tornar outros textos sobre C++, inclusive os de Stroustrup, mais fáceis de entender.

 

Esses tutoriais presumem que você conhece a linguagem C. Se esse não é o seu caso, gaste uma semana ou duas aprendendo C e então retorne a esses tutoriais. C++ é um superset do C, portanto quase tudo que você souber sobre C vai encontrar conexão nessa nova linguagem.

 

Porque C++ existe?

 

As pessoas que são novatas em C++, ou aqueles que lêem livros sobre C++, geralmente tem duas perguntas:

 

* “Tudo o que leio tem um vocabulário maluco: encapsulamento, herança, funções virtuais, classes, sobrecarga, amigos… De onde vem tudo isso?

* Essa linguagem - e programação orientada a objeto de um modo geral - obviamente implicam em uma mudança de mentalidade, então como eu faço para aprender a pensar em modo C++?

 

Ambas essas questões podem ser respondidas, e o projeto do C++ como um todo é facilmente inteligível, se você souber o que os projetistas do C++ pretendiam atingir quando criaram essa linguagem. Se você entender porque os projetistas fizeram as opções que fizeram, e porque introduziram certos recursos específicos na linguagem, então será muito mais fácil compreender a linguagem integralmente.

 

O projeto de linguagens de programação é um processo evolucionário. Uma nova linguagem é criada a partir de lições aprendidas com linguagens antigas, ou na tentativa de introduzir novos recursos e facilidades a uma linguagem existente. Por exemplo, a linguagem Ada foi projetada originalmente para resolver um problema aflitivo enfrentado pelo Pentágono. Os programadores, escrevendo código para diferentes sistemas de defesa militar, tinham usado centenas de linguagens de programação diferentes, o que tornaria, de fato, impossível manter ou aprimorar esses sistemas no futuro. Ada tenta resolver alguns desses problemas combinando os bons recursos de várias linguagens em uma única linguagem de programação.

 

Um outro bom exemplo é o processo evolucionário que ocorreu com as linguagens de programação a partir do desenvolvimento das linguagens estruturadas. Essas linguagens vieram em resposta a um grande problema não previsto pelos projetistas das linguagens de programação mais antigas: o uso abusivo do comando goto em programas muito grandes. Em um programa pequeno o comando goto não causa maiores problemas. Mas em um programa muito grande, especialmente quando desenvolvido por alguém viciado no comando goto, os problemas tornam-se terríveis. O código torna-se absolutamente incompreensível por um outro programador que tente lê-lo pela primeira vez. As linguagens de programação evoluíram para resolver esse problema, eliminando o comando goto inteiramente, e tornando simples subdividir um programa muito grande em módulos ou funções pequenas, compreensíveis e manejáveis.

 

C++ é uma linguagem orientada a objeto. Programação orientada a objeto é uma reação a problemas que foram percebidos pela primeira vez em programas muito grandes desenvolvidos na década de 70. Todas as linguagens orientadas a objeto tentam atingir três objetivos, como uma forma de impedir que ocorram os problemas inerentes a projetos muito grandes:

 

Todas as linguagens de programação orientadas a objeto implementam data abstraction de uma forma clara usando um conceito denominado classes. Vamos examinar data abstraction em maiores detalhes mais adiante, até porque esse é um conceito central, muito importante, em C++. Em poucas palavras, data abstraction é um modo de combinar dados e as funções usadas para manipulá-los, de tal forma que os detalhes da implementação fiquem ocultos para outros programadores. Data abstraction possibilita o desenvolvimento de programas mais fáceis de manter e de aprimorar. Todas as linguagens orientadas a objeto tentam tornar facilmente reutilizáveis e extensíveis cada uma das partes que compõem os programas. Aqui é que o termo objeto começa a fazer sentido. Os programas são quebrados, subdivididos, em objetos reutilizáveis. Esse objetos podem ser agrupados de diferentes maneiras para formar novos programas. Objetos existentes podem ainda ser estendidos. Dando aos programadores um modo muito simples de reutilizar código, e virtualmente forçando os programadores a escrever códigos para serem reutilizados, torna-se muito mais fácil desenvolver novos programas remontando peças já existentes.

 

Linguagens orientadas a objeto visam tornar um código existente facilmente modificável sem, na realidade, alterar fisicamente o código. Esse é um conceito único e muito poderoso, porque a primeira vista parece não ser possível modificar alguma coisa sem de fato alterá-la. Entretanto, é plenamente possível fazer isso usando dois novos conceitos: herança e polimorfismo. O objeto existente permanece o mesmo, e as alterações são como que assentadas sobre ele. A habilidade dos programadores para manter e aprimorar código de um modo livre de erros é drasticamente melhorada usando essa abordagem.

 

Como C++ é uma linguagem orientada a objeto, ela contém os três benefícios da orientação a objeto recém apresentados. C++ acrescenta ainda a tudo isso duas outras importantes melhorias, para eliminar problemas existentes na linguagem C original, e para tornar a programação em C++ mais fácil que em C.

 

C++ acrescenta um conceito denominado sobrecarga de operador. Esse recurso permite que você especifique em seus programas novos modos de uso para operadores padrões, tais como + e >>. Por exemplo, se você quiser adicionar um novo tipo de dado, como um tipo número complexo em um programa em C, tal implementação não será simples. Para somar dois números complexos, você terá que criar uma função denominada, por exemplo, soma, e então escrever c3=soma(c1,c2);, onde c1, c2 e c3 são valores do novo tipo número complexo. Em C++ você pode, ao invés de criar uma nova função, sobrecarregar os operadores + e =, de tal forma que você pode escrever c3=c1+c2. Dessa maneira, novos tipos de dados podem ser adicionados à linguagem de uma maneira clara, simples, sem ajeitamentos. O conceito de sobrecarga aplica-se a todas as funções criadas em C++.

 

C++ ainda simplifica a implementação de várias partes da linguagem C, principalmente no que se refere a operações de I/O e alocação de memória. Essas novas implementações foram criadas contemplando-se a sobrecarga de operadores, de tal modo que tornou-se fácil adicionar novos tipos de dados e providenciar operações de I/O e alocação de memória para os novos tipos, sem truques ou artifícios.

 

Vamos examinar alguns problemas que você provavelmente enfrentou usando a linguagem C, e então ver como eles são resolvidos pelo C++

 

O primeiro problema pode ser visto em toda biblioteca de programas construída em C. O problema é demonstrado no código a seguir, o qual atribui um valor a um string e então concatena esse valor a um outro string:

 

char s[100];

strcpy(s, “hello “);

strcat(s, “world”);

 

Essa codificação não é muito bonita, mas o seu formato ilustra muito tipicamente o que se encontra em bibliotecas criadas em C. O tipo string é construído a partir do tipo matriz-de-caracteres, que é nativo em C. Devido ao fato de que o novo tipo, string, não é parte integrante da linguagem original, o programador é forçado a usar chamadas de funções para fazer qualquer operação com o novo tipo de dado. O desejável é que, ao invés disso, se possa criar novos tipos de dados e lidar com eles naturalmente, com os próprios recursos da linguagem. Alguma coisa como

 

string s;

 

s = “hello “;

s += “world”;

 

Se algo assim é possível, a linguagem pode ser estendida ilimitadamente. C++ suporta esse tipo de extensão através da sobrecarga de operadores e de classes. Repare ainda que usando um novo tipo string, a implementação tornou-se completamente oculta. Ou seja, você não precisa saber como, ou se ,o tipo string string tem um tamanho máximo. Melhor ainda, é fácil alterar-se, no futuro, a implementação do tipo string, sem afetar negativamente os códigos que estiverem utilizando-o foi criado usando uma matriz de caracteres, uma lista ligada, etc. ou ainda se

 

Outro exemplo usando uma biblioteca pode ser visto na implementação de uma biblioteca para tratamento de pilhas.

 

Os protótipos das funções para uma típica biblioteca de tratamento de pilhas - normalmente encontrado no header file - é mostrado a seguir:

 

void stack_init(stack s, int max_size);

int stack_push(stack s, int value);

int stack_pop(stack s, int *value);

void stack_clear(stack s);

void stack_destroy(stack s);

 

O programador usuário dessa biblioteca pode usar funções para push, pop e clear a pilha, mas antes, e para que qualquer uma dessas operações seja válida, de inicializar a pilha com a função stack_init. Ao concluir com a utilização da pilha, deve destruí-la com a função stack_destroy. E o que acontece se você se esquece da inicialização ou da destruição? Em um caso real, o código não vai funcionar e pode ser bem difícil rastrear o problema, a menos que todas as rotinas dessa biblioteca detectem a falta da inicialização e indiquem isso especificamente. A omissão da etapa de destruição da pilha pode causar um problema denominado memory leak, que é também difícil de rastrear. C++ resolve esse problema usando construtores e destrutores, que automaticamente manejam a inicialização e a destruição de objetos, inclusive de pilhas.

 

Continuando ainda com o exemplo da pilha, note que a pilha, uma vez definida, pode push e pop números inteiros. O que acontece se você quer criar uma outra pilha para lidar com números reais, ou uma outra ainda para caracteres? Você terá que criar três bibliotecas separadas, ou, alternativamente, usar uma union e deixar a union manejar todos os tipos de dados possíveis. Em C++, um conceito denominado modelo permite que você crie uma única biblioteca para tratar a pilha e redefina os tipos de dados a serem armazenados na pilha quando esta for declarada.

 

Um outro problema que você já deve ter tido programando em C envolve a alteração de bibliotecas. Digamos que você esteja usando a função printf definida na biblioteca stdio, mas você quer modificá-la para manejar um novo tipo de dado que você criou em seu programa. Exemplificando, você deseja modificar printf para que ela imprima números complexos. Você não tem como fazer isso, a menos que você tenha o código fonte da implementação de printf. Mas ainda que você tenha o código fonte de printf, essa pode ser uma péssima estratégia porque você pode gerar um código não portável. Não há realmente um modo de extender facilmente uma biblioteca C uma vez que ela tenha sido compilada. Para resolver o problema de impressão de números complexos em C, como no nosso exemplo, você teria que criar uma nova função com a mesma finalidade que printf. Se você tiver vários novos tipos de dados, você terá que criar várias e diferentes novas funções de saída, à semelhança de printf. C++ lida com todos esses problemas com uma nova técnica para saída padrão. Uma combinação de sobrecarga de operador e classes permite integrar novos tipos de dados ao esquema padrão de I/O do C++.

 

Ainda pensando sobre a função printf, reflita sobre seu projeto e pergunte-se a si mesmo: Esse é um bom modo de se projetar um código? Dentro do código de printf há um comando switch, ou uma cadeia de if-else-if que avalia um string de formatação da saída. Um %d é usado para números decimais, um %c é usado para caracteres, um %s é usado para strings, e assim por diante. Há, no mínimo, três problemas com essa implementação:

 

O programador da implementação de printf tem que manter o comando switch, ou cadeia de if-else-if, e modificá-lo para cada novo tipo de formatação que se quiser implementar. Modificações sempre significam a possibilidade de se introduzir novos bugs.

 

Não há qualquer garantia de que o programador usuário de printf vai combinar corretamente o tipo do dado com string de formatação, o que significa que a função contém um risco de falha.

 

Essa implementação não é extensível, a menos que você possua o código fonte, você não pode ampliar as capacidades de printf.

 

C++ resolve esses problemas completamente porque força o programador a estruturar o código de uma nova maneira. O comando switch é ocultado e manejado automaticamente pelo compilador através da sobrecarga de função. Torna-se assim impossível combinar erradamente os parâmetros ao invocar uma função, primeiro porque eles não são implementados como parâmetros em C++, e segundo porque o tipo da variável controla automaticamente o mecanismo de switch que é implementado pelo compilador.

 

C++ resolve ainda vários outros problemas. Por exemplo, resolve o problema de código comum replicado em vários lugares, permitindo que você controle código comum em uma terceira dimensão. Resolve o problema eu quero alterar o tipo de dado passado para uma função sem alterar a função, permitindo que você sobrecarregue o mesmo nome de função com múltiplas listas de parâmetros. Resolve o problema eu quero fazer uma pequena alteração no modo como isso funciona, mas eu não tenho o código fonte, e ao mesmo tempo resolve o problema eu quero reformular essa função inteiramente, sem alterar o restante da biblioteca usando o conceito de herança.

 

C++ torna a criação de bibliotecas mais simples e melhora drasticamente a tarefa de manutenção do código. E muito mais.

 

Você precisará mudar um pouco o seu modo de pensar em programação para obter as vantagens de todos esses recursos poderosos, e isso significa que você vai ter que dedicar-se um pouco mais ao projeto de seu código. Sem isso, você perderá vários dos benefícios do C++. Como em tudo na vida, a migração para C++ significa custos e benefícios, mas nesse caso o conjunto dos benefícios supera em muito os custos.

 

 

Vocabulário C++

 

c1.jpg

 

Olhe o mundo a sua volta. Você pode entender uma grande parte da estrutura, do vocabulário e da organização do C++ apenas olhando a estrutura e a organização do mundo real, e refletindo sobre o vocabulário que usamos para falar sobre o mundo real. Muitos dos elementos do C++ - em da orientação a objeto em geral - tentam emular o modo como interagimos com o mundo real. Por exemplo, sempre que você olha em torno você vê uma grande quantidade de objetos. Nós organizamos esses objetos em nossas mentes arranjando-os em categorias, ou classes. Se você tem um livro em suas mãos, um livro é uma classe genérica de objetos. Você poderia dizer “esse objeto que eu tenho nas mãos é classificado como um livro.”Uma hierarquia de classes de objetos envolve a classe livro e a estende em duas direções. Livros são membros da classe mais geral publicações. Há ainda tipos específicos de livros, tais como livros de computação, livros de ficção, biografias, e assim por diante. A organização hierárquica se estende em ambos os sentidos: do mais geral para o mais específico. Em nosso exemplo, você tem nas mãos um determinado livro, um livro específico. No idioma OOP, você tem nas mãos uma instância da classe livro. Livros tem certos atributos que são comuns e portanto são compartilhados por todos os livros: uma capa, vários capítulos, não tem anúncios, etc. Livros tem também atributos comuns a publicações em geral: título, data de publicação, editora, etc. Tem ainda atributos comuns a objetos físicos: localização, tamanho, forma e peso. Essa idéia de atributos comuns é muito importante em C++. C++ modela o conceito de atributos comuns usando herança.

 

Há certas coisas que você faz com e para certos objetos, e essas ações são diferentes de objeto para objeto. Por exemplo, você pode ler um livro, folhear suas páginas. Você pode olhar um título, procurar um capítulo específico, pesquisar o índice, contar o número de páginas, etc. Essas ações são aplicáveis unicamente a publicações. Você não poderia folhear as páginas de um martelo, por exemplo. Entretanto, há ações que são genéricas e aplicáveis a todos os objetos físicos, como pegá-los. C++ também leva em conta esse fato e modela esses casos usando herança.

 

A natureza hierárquica das categorias de objetos, bem como nossa organização hierárquica de atributos de objetos e de ações, estão contidas na sintaxe e no vocabulário do C++. Por exemplo, quando projetando um programa você vai subdividi-lo em objetos, cada um dos quais tem uma classe. Você vai herdar atributos de uma classe base quando você criar uma classe derivada. Ou seja, você vai criar classes mais gerais de objetos e então fazer classes mais específicas, a partir das classes gerais, derivando o particular a partir do geral. Você vai encapsular o dados em um objeto com funções membro funções membro funções membro funções membro funções membro funções membro, e para ampliar classes você vai sobrecarregar e sobrescrever funções da classe base. Confuso? Vamos examinar um exemplo simples para ver o que esses termos significam na realidade.

 

O exemplo clássico de programação orientada a objeto é um programa gráfico que lhe permite desenhar objetos - linhas, retângulos, círculos, etc. - na tela do terminal. O que todos esses objetos tem em comum? Que atributos todos esses objetos compartilham? Todos tem uma localização na tela. Podem ter uma cor. Esses atributos - localização e cor - são comuns a todas as formas exibidas na tela. Portanto, como projetista do programa você poderia criar uma classe base - ou em outras palavras, uma classe genérica de objetos - para conter os atributos comuns a todos os objetos apresentados na tela. Essa classe base poderia ser denominada Forma, para melhor identificá-la como classe genérica. Você poderia então derivar diferentes objetos - círculos, quadrados, linhas - a partir dessa classe base, adicionando os novos atributos que são próprios de cada forma em particular. Um círculo específico desenhado na tela é uma instância da classe Círculo, que herdou uma parte de seus atributos de uma classe mais genérica denominada Forma. É possível criar tal conjunto de hierarquia em C, mas nem de longe com tanta facilidade quanto em C++. C++ contém sintaxe para tratar herança. Por exemplo, em C você poderia criar uma estrutura básica para conter os atributos localização e cor dos objetos. As estruturas específicas de cada objeto poderiam incluir essa estrutura básica e ampliá-la. C++ torna esse processo mais simples. Em C++, as funções são agrupadas, reunidas dentro de uma estrutura, e essa estrutura é denominada classe. Assim, a classe base pode ter funções, denominadas em C++ como funções membro, que permitam que os objetos sejam movidos ou re-coloridos. As classes derivadas podem usar essas funções membro da classe base tal como são, criar novas funções membro, ou ainda sobrescrever funções membro da classe base.

 

O mais importante recurso que diferencia C++ do C é a idéia de classe, tanto em nível sintático quanto em nível conceptual. Classes permitem que você use todas as facilidades de programação orientada a objeto - encapsulamento, herança e polimorfismo - em seus programas em C++. Classes são ainda a estrutura básica sobre a qual outros recursos são implementados, como sobrecarga de operador para novos tipos de dados definidos pelo programador. Tudo isso pode lhe parecer confuso ou desarticulado nesse momento, mas na medida em você de torne familiarizado com os conceitos e com esse vocabulário vai perceber todo o poder dessas técnicas.

 

 

Creditos :SlashGun & Eu

 

5hTkq8b.png

Link para o comentário
Compartilhar em outros sites

Este tópico está impedido de receber novos posts.
 Compartilhar

×
×
  • Criar Novo...

Informação Importante

Nós fazemos uso de cookies no seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies , caso contrário, vamos supor que você está bem para continuar.