notas-de-aula- Schneider
29 pág.

notas-de-aula- Schneider

Disciplina:Linguagens de Programação I252 materiais732 seguidores
Pré-visualização11 páginas
ainda, operações
padrão de atribuição e comparação de equivalência e desigualdade, à critério do programador, que
pode suprimi-las ou redefini-las.

A fim de restringir o acesso às partes de um TAD para o cliente e ao mesmo tempo oferecer essa
informação ao compilador, Ada usou uma palavra especial (private) para modificar declarações,
informando que elas ficam inacessíveis ao cliente.

A linguagem Modula-2 oferece “módulos” que são muito semelhantes aos “pacotes” de Ada, com a
restrição de que os tipos definidos em módulos são sempre ponteiros. Dessa forma, o conhecimento
da estrutura interna dos módulos não é necessária nem mesmo ao compilador e as declarações
separadas das implementações não são necessárias.

prof. Bruno Schneider 25

Notas de Aula de GCC105 - BCC e BSI - UFLA

Em C++ a estrutura que proporciona encapsulamento é a classe. Classes são tipos de dados. A
ocultação de informações funciona de maneira semelhante à da Ada. Essas duas características
juntas proporcionam um suporte mais direto aos TADs. Outras facilidades oferecidas são os
construtores e destrutores que podem ser usados para gerenciar a inicialização, alocação e
desalocação de recursos (ex.: memória no heap).

O suporte de Java é muito parecido como o de C++, com algumas diferenças importantes. Todos os
tipos definidos pelo programador em Java devem ser classes. Todas as variáveis que são TADs são
alocadas no heap e acessadas por meio de referências. Além das classes, Java permite a definição de
pacotes (encapsulamento geral) e oferece o “escopo de pacote”, em que partes protegidas das
classes são acessíveis em outras partes do pacote.

2.6.3 Tipos abstratos de dados parametrizados
Assim, como os subprogramas, tipos de dados parametrizados são de grande utilidade. Eles
permitem generalizar tipos de dados, descrevendo tipos como “lista de qualquer coisa”.

Ada, C++ e Java são exemplos de linguagens que proporcionam esse tipo de construção. Assim
como nos subprogramas, elas usam o código como modelo para a construção de vários pacotes ou
classes conforme os casos são instanciados.

2.7 Tratamento de erros
A execução de programa está sujeita a vários tipos de erros. Alguns erros acontecem ao nível de
hardware (ex.: overflow aritmético) e outros acontecem ao nível de software (ex.: tentar remover
elemento que não existe numa coleção). Esse último tipo pode ser tratado pelo compilador (quando
possível) ou pela própria execução do programa.

As linguagens de programação devem facilitar o tratamento dessas condições de erro, facilitando a
tarefa de produzir programas que lidam graciosamente com elas.

Algumas linguagens determinam que seja gerado código para detecção de erros em várias situações
(ex.: valor inválido para o índice de um vetor), mas é desejável que permitam ao programador
especificar o que deve ser feito nas situações de erro.

2.7.1 Código de Erros
A forma mais primitiva para lidar com erros é usar seletores que fazem uso das formas disponíveis
para detecção de erros. Assim, as instruções de um programa são frequentemente seguidas de
instruções que desviam o fluxo de instruções caso uma situação anormal seja detectada.

O uso de funções que relatam algum tipo de status da execução é comum na hora de projetar
subprogramas.

Programas também retornam esse status de tal forma que os programas que os chamaram também
possar verificar problemas na execução para tomar as medidas necessárias. Dessa forma, linguagens
como C++ exigem que o bloco principal de um programa retorne um número inteiro. Por
convenção, esse número deve ser zero para indicar um execução sem erros, enquanto que qualquer
outro número indica algum tipo de erro de execução. Cada programa cria o significado dos valores
que indicam erros, muitas vezes, tratando o valor como um vetor de bits em que cada bit indica a
ocorrência de um tipo de erro.

O uso de funções que retornam status tem as desvantagens de (a) criar um código em que a

prof. Bruno Schneider 26

Notas de Aula de GCC105 - BCC e BSI - UFLA

condições anormais ficam misturadas com a condição normal e (b) dificultar o projeto de funções
que retornam o resultado de um processamento (além do status). A segunda pode ser reduzida pela
tipificação dinâmica, que permite a criação de uma função que retorna algum tipo que representa
erro ou um tipo que representa o resultado do processamento.

Nas bibliotecas padrão da linguagem C, o valor de retorno de um subprograma é geralmente usado
como código de erro.

2.7.2 Manipulação de Exceções
Algumas linguagens oferecem construções para tratar condições anormais de execução detectadas
por hardware e software, inclusive condições incomuns (não errôneas) em geral de uma mesma
forma. O uso dessas construções é chamado de manipulação de exceções.

Essas construções tem a desvantagem de adicionar uma boa dose de complexidade à linguagem e
consequentemente ao seu compilador. A vantagem da manipulação de exceções é o aumento da
legibilidade e a facilidade para descrever as formas de tratar erros, o que funciona como um
incentivo ao tratamento de erros.

2.7.2.1 Questões de projeto
A manipulação de exceções deveria proporcionar formas de reusar código para tratamento de
exceções (ex.: se qualquer uma dessas divisões der erro faça aquilo) ao mesmo que fornece formas
de ligar tratadores a casos especiais de execução (ex.: essa divisão por zero é especial e deve ser
tratada de forma diferente).

É desejável permitir que o tratamento de exceções seja ligado e desligado tanto em tempo de
execução quanto em tempo de compilação.

Outras questões de projeto da linguagem incluem:

• como e onde especificar os tratadores (subprogramas? trechos de código?), além de como
determinar seu escopo;

• como vincular a ocorrência de uma exceção à execução do tratador;

• como e onde retomar a execução do programa depois do tratamento;

• como o usuário pode especificar exceções;

• a existência de tratadores padrão;

• a criação automática de exceções para condições comuns de erro;

• a geração de exceções predefinidas pelo próprio programa;

• a possibilidade de desativar e reativar exceções.

Uma forma popular de vincular a ocorrência de uma exceção à execução do tratador é conhecida
como propagação de erro. Ela consiste em checar se houve erro na execução de algum subprograma
e então repassar essa indicação para o chamador. Essa passagem que é feita manualmente usando
códigos de erro é feita automaticamente pela manipulação de exceções.

2.8 Sistemas de tipo
A checagem de tipos é, em geral, benéfica. As linguagens precisam de sistemas para garantir a

prof. Bruno Schneider 27

Notas de Aula de GCC105 - BCC e BSI - UFLA

checagem, preferencialmente, em tempo de compilação. Portanto, um sistema de tipos (no contexto
de linguagens de programação) é o conjunto de regras que permite a classificação de expressões de
acordo com um tipo.

Declarar o tipo de cada variável antes do seu uso é uma forma simples de proporcionar isso, mas
oferece pouca flexibilidade para o programador, limitando o reuso e a facilidade de manutenção.
Para conciliar essas duas necessidades contraditórias, pode-se usar declarações mais gerais, em que
não se define um tipo por completo, mas que fornecem elementos suficientes para a checagem.

O conceito de polimorfismo (tratamento de tipos diferentes de maneira uniforme) é a flexibilidade
desejada e é difícil de conciliar com a verificação de tipos.

Nas linguagens em que os tipos precisam ser completamente determinados, temos monomorfismo.
Naquelas em que o tipo precisa ser parcialmente determinado (somente o suficiente para checagem
de tipos nas expressões) ou em que não há sistemas de tipos, temos polimorfismo.

O polimorfismo pode então ser obtido em níveis variados, ou seja, as linguagens proporcionam
mais ou menos recursos para o tratamento homogêneo de tipos diferentes. Alguns