Tipos de Dados
 Tipagem forte e fraca
 Representação de tipos
 Equivalência, conversão e inferência de tipos
 Classes x Tipos
 Composição de tipos
 Extensão de tipos
Tipagem forte e fraca
Linguagens fortemente tipadas: o compilador e
o ambiente de execução se encarregam de
fazer a verificação de tipos
Verificação de tipos: operandos, operadores,
atribuições, subprogramas
Verificação estática: compilador
Verificação dinâmica: ambiente de execução
Uma linguagem que não garante a ausência de
erros de tipos, pelo menos de forma estática
(em tempo de compilação) é dita fracamente
tipada.
Violação de tipos
Um dos principais objetivos da utilização de sistemas de
tipos em linguagens de programação é permitir a
detecção de erros (de tipos).
Considera-se um erro toda e qualquer violação das
regras definidas pela linguagem de programação.
A detecção de erros pode ser feita de forma estática, ou
de forma dinâmica, caso seja possível a detecção
somente durante a execução de programa. Exemplos:
Estática: violação de tipo por atribuição
Dinâmica: violação de tipo por leitura, com valor proveniente do
meio externo, somente poderá ser detectada pelo ambiente de
execução.
Exemplo: tipos de dados em Pascal
Como
representar?
primitivos:
simples
- integer - real
- char
- boolean
enumeração
restrição
TIPOS
Como
compatibilizar?
registro/ registro variante
estruturados
array
set
arquivo
Exemplo de violação estática e dinâmica
program tt;
type
natural=1..maxint;
var
i:integer;
s:shortint;
n:natural;
begin
compilador acusa
i:=10;
erro de tipo
s:=-10;
n:=i;
n:=s; {violação dinâmica de tipo}
writeln(n);
n:= -10; {violação estática de tipo}
i:=maxint;
writeln(i);
s:=i;
{violação dinâmica de tipo}
writeln(s);
end.
Exemplo de violação estática e dinâmica
program tt;
type
natural=1..maxint;
resultados:
var
i:integer;
-10
s:shortint;
32767
n:natural;
begin
-1
i:=10;
s:=-10;
n:=i;
n:=s; {violação dinâmica de tipo}
writeln(n);
n:= -10; {violação estática de tipo}
i:=maxint;
writeln(i);
s:=i;
{violação dinâmica de tipo}
writeln(s);
end.
Portabilidade x representação de tipos
Pessoas que apregoam não se preocupar com
portabilidade geralmente fazem isto porque usam um
único sistema e sentem que podem dar-se ao luxo de
achar que “a linguagem é aquilo que meu compilador
implementa”
Esta é uma visão restrita e míope. Se seu programa é
um sucesso, é muito provável que seja portado, de modo
que alguém vai ter que procurar e corrigir problemas
relacionados com características dependentes da
implementação.
 Além disto, freqüentemente é necessário compilar
programas com outros compiladores para o mesmo
sistema e mesmo uma versão futura de seu compilador
favorito pode fazer algumas coisas de maneira diferente
da maneira atual.
Bjarne Stroustrup, criador de C++
Tamanhos de tipos: C++
Os tamanhos dos objetos de C++ são expressos em
múltiplos do tamanho de um char
Um char pode armazenar um caractere do conjunto de
caracteres da máquina
É garantido que um char tem pelo menos 8 bits, um
short pelo menos 16 bits e um long pelo menos 32 bits
Por definição, o tamanho de char é 1
O tamanho de um objeto ou tipo pode ser obtido usando
o operador sizeof
1
sizeof(char)
sizeof(short)
sizeof(int)
sizeof(long)
Representação de tipos
A representação interna pode ser
direta, refletindo a própria estrutura lógica do
hardware, ou
indireta, quando a variável tem sua representação
interna associada a um descritor de tipo
A representação interna depende do projeto da
linguagem de programação e da arquitetura da
máquina onde é feita a implementação da
linguagem. Por exemplo, a representação de
reais
em FORTRAN é direta
em LISP um real é representado por uma seqüência
de bits associada a um descritor que especifica que
aquela seqüência deve ser tratada como um número
real
Representação direta e indireta
Direta: exemplo de ponto flutuante (padrão IEEE)
expoente
bit sinal
fração decimal
8 bits
23 bits
Indireta: apontador para a representação e/ou descritor de tipo
Tipo de dado: vetor
descritor
Limite inferior : Li
Limite superior : Ls
Tipo dos elementos
Apontador para a representação
dados
Equivalência, compatibilidade e
inferência
A equivalência de tipos existe quando dois
valores possuem o mesmo tipo
Exemplo: type natural: 1.. maxint;
var n1, n2: natural;
 A compatibilidade define quando um tipo pode
ser usado em lugar de outro em um
natural ou
determinado contexto
integer?
Exemplo: var i: integer; ........ i := n1 + 10;
A inferência de tipos define o tipo de uma
expressão com base nos tipos de operandos
envolvidos e a operação a ser realizada
Equivalência de tipos
Tipos estruturados e tipos definidos pelo
usuário: quando são considerados equivalentes?
Formas de equivalência:
Equivalência estrutural: mesma representação
Equivalência de nome: mesmo nome de tipo
Equivalência de declaração: mesma declaração
Exemplo:
type
V1: array[1..10] of integer;
V2: array[1..10] of integer;
A : V1;
B, C : V2;
D : V2;
Exemplo em Pascal: parte 1
program equitipos;
type
V1= array[1..10] of integer;
V2= array[1..10] of integer;
procedure testaV(var vet1:V1; vet2:V2);
var
i: byte;
begin
for i:=1 to 10 do vet2[i]:=vet1[i];
end; {function}
Exemplo em Pascal: parte 2
var
A : V1;
equivalência
D : V2;
de nome
vetT:array[1..10] of integer;
ind:byte;
begin
for ind:=1 to 10 do
begin
A[ind]:= ind * 10;
D[ind]:= A[11 - ind];
end;
testaV(A, D);
Compilação:
testaV(D,A);
testaV(A,vetT);
erro de tipo
end.
Exemplo de equivalência de registro
type doiscampos = record
a, b: integer;
end;
type doiscampos = record
a: integer;
b: integer;
end;
?
type doiscampos = record
b: integer;
a: integer;
end;
Exemplo em Pascal: objetos e registros
program doiscampos;
var
r1: record
a,b:integer;
end;
r2: object
b, a: integer;
end;
begin
r1.a := 10;
r1.b := 20;
{ r2:= r1; compilação: erro de tipo}
r1 := r1;
end.
Compatibilidade de tipos
Existem situações em que valores de um
determinado tipo são esperados:
Atribuição: var = expressão
Operação: var1 / var2
Parâmetros: fun (arg1, arg2)
Questão: como as LP lidam com a
compatibilidade de tipos?
Compatibilidade de tipos
Quando existe compatibilidade de tipos, podese misturar valores de tipos diferentes em
expressões, ou mesmo usar um tipo A em lugar
de um tipo B, sem que haja violação das regras
de segurança de tipos (erro de tipo)
Exemplos com tipos primitivos:
Seja i um inteiro e f um real e fun (inteiro, real)
São válidas as atribuições: i := r e r:= i?
É válida a invocação: fun(r, i)?
Compatibilidade: exemplo em Pascal
program testacomp;
type
dd=byte;
procedure testaDias(dias:byte);
begin
writeln(dias);
end;
var
diasr:1..31;
diasi:integer;
diasb:dd;
begin
diasr:=14; diasi:=15; diasb:=16;
{testaDias(diasr); erro estático}
{testaDias(diasi); erro estático}
{testaDias(1234); erro estático}
testaDias(diasb);
end.
Conversão de tipos
Quando os tipos envolvidos em uma expressão
não são equivalentes, a linguagem oferece a
possibilidade de conversão de tipos
A conversão implícita ou automática é feita com
base nas regras definidas sem a interferência
do programador
A conversão explícita é codificada diretamente
pelo programador.
 Termos em inglês: casting, coercion
Conversão de tipos primitivos
A conversão de tipos primitivos é ditada pelas
regras da linguagem de programação
 Normalmente admite a conversão de um tipo
de representação menor para um tipo de
representação maior
Por exemplo, em Java, a conversão entre tipos
primitivos - exceto o tipo bool que não admite
conversão - considera o tamanho do tipo (8, 16,
32, 64 bits)
Conversão: menor para maior
de
para
byte
short,int,long,float,double
short
int,long,float,double
char
int,long,float,double
int
long,float,double
long
float,double
float
double
Formas de conversão
A conversão pode ser feita de três maneiras:
por atribuição, ou conversão implícita para o
tipo do lado esquerdo do comando de atribuição
por promoção aritmética para o tipo de
resultado esperado da operação (inferência)
por conversão explícita (casting).
Seja float f, int i, float r;
exemplo de atribuição: f = i;
exemplo de promoção: r = f / i;
exemplos de casting: i = (int) f ;
r = (float) i/f;
Classes como tipos de dados
Uma definição de classe cria um novo tipo de
dado
Membros de uma classe: variáveis (campos) e
métodos (funções)
Formato simplificado de definição de classe
Java
class NomeDaClasse
{ // inicia corpo da definição
// definição dos campos
// definição das funções (opcionais)
} // fim da classe NomeDaClasse
Registros em Java = Classes
Não existem estruturas de registros como
records (pascal) e struct (C)
Uma estrutura de registro deve ser definida
como uma classe, sendo:
nome da classe = nome da estrutura
variáveis da classe = campos do registro
instâncias da classe = variáveis do tipo registro
class Ator {
String nome;
int idade;
char sexo;
}
Ator
nome | idade | sexo
Pascal x Java
type
Tnome = object
sobre:Tstring;
meio: Tstring;
prim: Tstring;
end;
var
meuNome,teuNome:
Tnome;
.....
{referência:}
meuNome.prim:= ....
class Tnome {
String sobre;
String meio;
String prim;
}
.....
Tnome meuNome,
teuNome;
......
// referência:
meuNome.prim = ....;
Exemplo em Java
registro
class DoisCampos{
int a, b;
}
class TestaDoisCampos{
public static void main(String args[]){
DoisCampos r1,r2;
r1 = new DoisCampos();
instâncias
r2 = new DoisCampos();
r1.a=10; r1.b=20;
System.out.println("r1: "+ r1.a +"\t"+ r1.b);
System.out.println("r2: "+ r2.a +"\t"+ r2.b);
}}
Equivalência de nome: exemplo
class Pessoa {
String nome;
int idade;
char sexo;
}
class Aluno {
String nome;
int idade;
char sexo;
}
Composição de tipos
Composição de tipos: quando um componente
de um tipo estruturado é também um tipo
estruturado, como por exemplo, arrays de
registros ou arrays de arrays.
Utilizam a definição de tipos como base da
composição
Úteis em estruturas de dados mais complexas,
permitindo:
reduzir o esforço de programação e
aumentar a legibilidade de programas
Composição: exemplo em Pascal
begin
r1.a := 10;
r1.b := 20;
r3:=r1;
writeln(r3.a,tab,r3.b);
r2.a:= r1.a;
10 20
r2.b:= r1.b;
10 20 10 20
r2.c := r3;
writeln(r2.a,tab,r2.b,tab,r2.c.a,tab,r2.c.b);
end.
type
t1= record
a,b:integer;
end;
t2= record
b, a: integer;
c: t1;
end;
var
r1,r3: t1;
r2: t2;
Hierarquia de composição de registros
type
Tstring = string[20];
Tsexo = (masculino, feminino);
Tnome = record
sobrenome : Tstring;
meio: Tstring;
primeiro: Tstring;
end;
Tpessoa= record
nome:Tnome;
sexo:Tsexo;
email:Tstring;
end;
Tpessoa
nome
sobrenome
string
meio
string
primeiro
string
sexo
email
masc/fem
string
Composição: vários níveis
Tpessoa= record
nome: Tnome;
sexo: Tsexo;
email: Tstring;
end;
Taluno= record
matric:Tstring;
ident: Tpessoa;
end;
Taluno
TString
Tnome
sobre
Tpessoa
Tsexo
meio
Referência: Taluno.Tpessoa.Tnome.prim
Tstring
prim
Composição: exemplo de uso
var
Turma: array[1..2] of Taluno;
Professor1, Professor2: Tpessoa;
begin
Professor1.nome.sobrenome:= ' ';
Turma[1].ident.nome.sobrenome := ' ';
Professor1.email:= ' inf.ufrgs.br';
Professor2.email := Professor1.email;
Turma[2]:=Turma[1];
end.
instanciação
referência
qualificada
cópia:
mesmo TIPO
{ Professor1 := Turma[2]; >>> Erro de tipo }
{ Turma[2] := Professor1; >>> Erro de tipo }
Composição de tipos com classes
class Pessoa{
String nome;
int idade;}
class Novela{
Pessoa autor;
String titulo; }
String
nome
idade
Pessoa
autor
classe
campo
titulo
Novela
Extensão de tipos
Extensão de tipos: quando um um tipo
estruturado usa como base outro tipo
estruturado
Utilizam o mecanismo de herança
um mecanismo de criação de novos tipos de dados
Úteis em estruturas de dados mais complexas,
permitindo:
reduzir o esforço de programação
aumentar a reutilização de código e
aumentar a legibilidade de programas
Extensão: exemplo em Pascal
Tnome = object
sobrenome : Tstring;
meio: Tstring;
primeiro: Tstring;
end;
Tpessoa= object(Tnome)
sexo:Tsexo;
email:Tstring;
end;
Taluno= object(Tpessoa)
matric: Tstring;
end;
Questão: é correto derivar
Tpessoa de Tnome?
todos os campos
de Tnome + sexo
+ email
todos os campos de
Tpessoa + matric
Extensão de tipos em Pascal:
instanciação
type
Taluno = object(Tpessoa)
Tstring = string[20];
matric: Tstring;
Tsexo = (masculino, feminino);
end;
Tnome = object
Tfunc = object(Tpessoa)
sobrenome : Tstring;
secao:Tstring;
meio: Tstring;
anoadm:integer;
primeiro: Tstring;
end;
end;
var
Tpessoa = object(Tnome)
Nome: Tnome;
sexo:Tsexo;
Pessoa:Tpessoa;
email:Tstring;
Aluno1, Aluno2: Taluno;
end;
Funcionario: Tfunc;
Extensão: utilização
var
Mensagem: Tstring;
Turma: array[1..2] of Taluno;
Professor1, Professor2: Tpessoa;
begin
Mensagem := ' Modelo de Objetos';
Professor1.sobrenome:= ' Pardal ';
Turma[1].sobrenome := ' Luizinho ';
Professor1.email:= ' inf.ufrgs.br';
Professor2.email := Professor1.email;
Turma[2]:=Turma[1];
end.
instanciação
Professor2 := Turma[1];
writeln(Professor2.sobrenome);
referência
qualificada
cópia:
tipo/subtipo
Extensão de tipos em Java:
relacionamentos entre classes
class TNome {
String primeiro="Pedro",meio="Alvares",
sobrenome="Cabral";}
Extensão ou
Composição?
class TPessoa extends TNome{
String email= "pessoa@";
char sexo='m';}
class TAluno extends TPessoa{
int matricula=9999;}
class TAtor extends TPessoa{
public String contrato= "MTV";}
Extensão de tipos em Java: instanciação
class Pessoas {
public static void main (String[] args){
/* declarando objetos */
TNome nome;
TPessoa pessoa;
TAluno aluno;
TAtor ator;
/* criando objetos */
nome = new TNome();
pessoa = new TPessoa();
aluno= new TAluno();
ator = new TAtor();
System.out.println(" Nome " + nome);
System.out.println(" Pessoa " + pessoa);
System.out.println(" Aluno " + ator);
System.out.println(" Ator " + aluno);
Extensão de tipos: instanciação
System.out.println("
System.out.println("
System.out.println("
System.out.println("
Nome
Pessoa
Ator
Aluno
Pedro
Pedro
Pedro
Pedro
Nome " + nome);
Pessoa " + pessoa);
Ator " + ator);
Aluno " + aluno);
Alvares Cabral
Alvares Cabral pessoa@ m
Alvares Cabral pessoa@ m MTV
Alvares Cabral pessoa@ m 9999
Modelo de Objetos: equivalência
Herança estabelece uma
Um subtipo “é-um” tipo
hierarquia (ou família) de tipos
para fins de equivalência
Atribuições válidas
somente de subtipos
Oa
Tipo
para tipos.
A
Exemplos válidos
Oaa
Oa = Oab
Oab
Subtipo
Subtipo
Oa = Oabc
AA
AB
Exemplos inválidos
Oabc
Subtipo
ABC
Oaa = Oab
Oabc = Oab
Conversão (implícita) na hierarquia
Seja a hierarquia:
Atribuições válidas de
objetos:
nome=pessoa
pessoa=ator
pessoa=aluno
Atribuições inválidas de
objetos:
pessoa=nome
ator=aluno
Nome
upcast
Pessoa
Ator
downcast
Aluno
Exemplos de conversão implícita
pessoa.primeiro="Pero";
pessoa.meio="Vaz";
pessoa.sobrenome="Caminha";
nome= pessoa;
System.out.println(" Nome " + nome);
pessoa= aluno;
pessoa= ator;
aponta para um
tipo TPessoa
Nome Pero
Vaz Caminha pessoa@ m
Conversões explícitas hierarquia
Questão: como tornar válidas
as atribuições de objetos como
os abaixo?
aluno = pessoa;
ator=aluno
Solução:
usar “casting” de tipo
Pessoa pessoaRef;
Aluno alunoRef;
alunoRef = (Aluno) pessoaRef;
Nome
Pessoa
Ator
Aluno
Exemplo de conversão explícita
pessoa = (TPessoa)nome;
System.out.println(" Pessoa " + pessoa);
aponta para um
tipo TPessoa
Nome Pero
Vaz Caminha pessoa@ m
Agrupando objetos equivalentes
0
1
2
3
Nome
Pessoa
Nome
Pessoa
Ator
Aluno
Princípio: equivalência de tipos
Ator
Aluno
Agrupando objetos equivalentes
Agrupando em „arrays‟: mesmo tipo/subtipo
Exemplo: Nome grupo[]=new Nome[4];
0
1
2
3
Pessoa
Ator
Aluno
é um
Nome
é um
Nome
é um
Nome
Nome
Nome
Exemplo de grupo
TNome grupo[]= new TNome[4];
grupo[0]=nome;
grupo[1]=pessoa;
grupo[2]=ator;
grupo[3]=aluno;
for(int i=0;i<4;i++){
System.out.println(grupo[i]);}
Fim
Pedro
Pedro
Pedro
Pedro
Alvares Cabral
Alvares Cabral pessoa@ m
Alvares Cabral pessoa@ m MTV
Alvares Cabral pessoa@ m 9999
Download

Tipos de Dados