Professional Documents
Culture Documents
2. possvel implementar, para cada tipo primitivo, funes em JAVA nas quais
sejam trocados os valores dos seus parmetros formais? Caso sua resposta seja
afirmativa, implemente uma dessas funes e explique como funciona, destacando
como a troca feita. Em caso de resposta negativa, justifique. Existiria alguma
diferena na sua resposta caso a troca fosse realizada entre parmetros de um
mesmo tipo objeto? Justifique.
No. Java optou por fazer a passagem de tipos primitivos sempre por cpia para o
parmetro formal, o que impede que uma troca entre os parmetros formais se reflita
nos parmetros reais. No haveria diferena significativa na resposta se a troca fosse
realizada entre parmetros de um mesmo tipo objeto. Embora JAVA faa a passagem
de parmetros dos tipos objeto (no primitivos) por cpia de referncia, os efeitos da
troca continuariam sendo vlidos apenas internamente a funo, no se reflitindo nos
parmetros reais.
pode ser til? Compare essa abordagem geral de JAVA com a adotada por C e
C++ em termos de redigibilidade e legibilidade.
A funo System.out.println de Java recebe sempre uma string como argumento. Ela
funciona de forma similar a printf por conta do uso combinado da operao de
concatenao (+) de strings e da realizao de converso implcita de tipos antes da
operao de concatenao. Tipos primitivos e objetos so convertidos implicitamente
para strings antes da realizao da concatenao (quando o outro argumento uma
string). Por exemplo, na chamada de System.out.println seguinte
int i =3;
System.out.println (teste: + i + : + new Float (4.3) + : + true);
o valor 3 de i convertido na string 3 antes de ser concatenado string teste.
Observe que esse processo se repete antes da realizao de cada operao de
concatenao. Em particular, o objeto Float criado convertido para uma string
atravs da chamada implcita da operao toString() dessa classe. Note que o valor
booleano (primitivo) tambm convertido implicitamente na string true.
Essa abordagem de JAVA pode causar confuses e erros. Na chamada seguinte de
System.out.println
int i =3;
int j =5;
System.out.println (i + j);
no haver converso de tipo e concatenao de strings. Como nenhum dos dois
argumentos string, JAVA far primeiro a operao aritmtica e depois a converso
do resultado para string, resultando na impresso da string 8 em vez de 35, como
algum pode eventualmente esperar.
O problema da falta de lista de parmetros varivel pode ser contornado de maneira
geral em JAVA atravs da utilizao de um parmetro formal vetor no qual no se
especifica o nmero de elementos do vetor. Se a funo recebe elementos de um
mesmo tipo, isso d flexibilidade para passar uma quantidade varivel de valores para
a funo em cada chamada. Se a funo necessita receber elementos de tipos
diferentes, basta definir o parmetro formal como um vetor de objetos. Essa soluo
to geral quanto a soluo de C e C++ e mais legvel, uma vez que no necessria
a utilizao de macros pr-definidas, cujo comportamento no amplamente
conhecido, como o caso em C e C++. Por outro lado, a redigibilidade um pouco
prejudicada pela necessidade de criar o vetor de objetos com os elementos que
precisam ser passados para a funo antes da sua chamada. A soluo mais geral de
JAVA pode ser observada no trecho de cdigo abaixo:
void f(Object[] x) {
for (int i = 0; i < x.length; i++) System.out.println (x[i]);
}
void g() {
Object[] a = new Object[] {
new Integer(12), new Float(1.5), new Boolean(true) };
f(a);
a = new Object[] {
new Integer(1), new Byte(15) };
f(a);
}
Determine qual o valor das variveis valor e lista aps a execuo do programa,
supondo que:
11. Os dois trechos de cdigo seguintes apresentam definies (em arquivos .h) do
tipo abstrato de dados BigInt em C e C++, respectivamente. BigInt um tipo de
dados que permite a criao de nmeros inteiros maiores que long.
// C
struct BigInt {
char* digitos;
unsigned ndigitos;
};
struct BigInt criaBigIntC (char* c);
// cria a partir de string
struct BigInt criaBigIntN (unsigned n);
// cria a partir de unsigned
struct BigInt criaBigIntB (struct BigInt b); // cria a partir de outro BigInt
void atribui (struct BigInt* b1, struct BigInt* b2);
struct BigInt soma (struct BigInt b1, struct BigInt b2);
void imprime (FILE* f, struct BigInt b);
void destroi (struct BigInt b);
// C++
class BigInt {
char* digitos;
unsigned ndigitos;
public:
BigInt (const char* c);
BigInt (unsigned n = 0);
BigInt (const BigInt& b);
void atribui (const BigInt& b);
BigInt soma (const BigInt& b) const;
void imprime (FILE* f = stdout) const;
~ BigInt();
};
12. Ao se modificar a estrutura de dados de uma classe em C++, ainda que mantida a
mesma interface (isto , as assinaturas das operaes pblicas da classe continuam
idnticas s existentes antes da alterao), necessrio recompilar no apenas o
cdigo da prpria classe, mas tambm os programas usurios dessa classe.
Explique porque isso ocorre levando em conta que no h alteraes no cdigo
fonte dos programas usurios. Explique como JAVA evita a necessidade de
recompilao nesses casos. Apresente razes para justificar a no incorporao
dessa caracterstica em C++?
A recompilao do cdigo usurio necessria pois a alterao na estrutura de dados
modifica o tamanho do espao a ser armazenado pelo cdigo usurio dessa estrutura.
JAVA sempre aloca objetos no monte, logo o cdigo usurio s necessita alocar uma
referncia para o objeto na pilha. Como a interface com o construtor no foi alterada e
ele quem aloca o espao no monte, no haver problemas na mudana da estrutura.
C++ no incorpora o mecanismo implementado em JAVA porque a alocao no
monte tem desempenho menos eficiente que a alocao na pilha.
10
valor alterado para 8. Logo, ao final da segunda chamada a = {3, 8, 10} e o valor
impresso 10.
A execuo desse cdigo produz um efeito estranho prejudicial legibilidade. Ocorre
sinonmia quando a varivel i assume o valor 1, no for, pois os dois parmetros
formais da funo incrementa so associados ao mesmo parmetro real a[1], o que
dificulta o entendimento do programa.
14. Em uma LP, o momento da avaliao dos parmetros reais, durante a passagem de
parmetros, pode ser por definido pelo modo normal (eager), por nome (by name)
ou preguioso (lazy). Explique o significado de cada um desses modos. Execute
trs vezes o programa C seguinte, supondo que a linguagem adotasse, em cada
execuo, um tipo diferente de modo de avaliao. Explique os resultados
alcanados.
void avalia (int c) {
int i;
for (i = 0; i < 3; i++) {
printf ("%d\n", c);
}
}
void main ( ) {
int j = 0;
avalia (j++);
}
No modo normal (ou eager) a avaliao dos parmetros reais ocorre no momento da
chamada do subprograma. No modo por nome (by name) a avaliao ocorre em todos
os momentos em que o parmetro formal usado. No modo preguioso (lazy) a
avaliao ocorre no primeiro momento em que o parmetro formal usado.
Execuo pelo modo normal (eager):
Resultado:
0
0
0
Como a avaliao dos parmetros normal, o valor passado para c ser resultado da
avaliao retornado pela expresso j++ antes da execuo de avalia. Portanto, c = 0.
Execuo pelo modo por nome (by name):
Resultado:
0
1
2
Explicao: No modo de avaliao por nome, no momento em que o parmetro
formal c utilizado pela primeira vez na funo avalia, tem-se c = 0 e j torna-se 1.
Na utilizao de c pela segunda vez tem-se c = 1 e j torna-se 2. Na utilizao de c
pela terceira vez tem-se c = 2. O valor de j muda a cada avaliao do parmetro
real, o que acontece sempre que o parmetro formal utilizado. Isso faz com que o
valor do parmetro formal impresso seja diferente a cada impresso.
Execuo pelo modo preguioso (lazy):
Resultado:
0
0
11
0
Na avaliao preguiosa, o parmetro real j++ avaliado no momento em que o
parmetro formal c utilizado pela primeira vez e permanece assim at o final da
execuo da funo. Logo, c = 0 em todas os passos da repetio.
15. Descreva como deve ser feita a operao de desalocao de memria em um tipo
abstrato de dados lista de inteiros em C, C++ e JAVA. Compare as diferentes
abordagens adotadas por essas LPs na implementao e uso dessa operao em
termos de redigibilidade, confiabilidade e eficincia.
Em C necessrio que o programador crie uma funo para percorrer cada n da lista
e v desalocando-os. Essa funo deve ser chamada explicitamente quando a lista
necessita ser desalocada. C++ utiliza uma funo especial destrutora da classe, a qual
deve ser implementada pelo programador. Essa funo chamada implicitamente
quando se encerra o tempo de vida da lista. Em JAVA, a desalocao feita
automaticamente pelo coletor de lixo, no existindo necessidade de implementao de
uma operao destrutora ou invocao de qualquer mtodo.
Em termos de redigibilidade, a melhor soluo a de JAVA pois o programador no
necessita redigir nada. C++ requer a implementao da funo destrutora, mas no
necessita da redao de cdigo para chamada dessa funo. C a soluo de menor
redigibilidade pois o programador necessita implementar a funo de desalocao e
tambm cham-la explicitamente no cdigo usurio.
Em termos de confiabilidade, a soluo de JAVA tambm superior, pois o
programador no necessita definir como os ns lista devem ser desalocados, nem
quando isso deve ser feito. O prprio sistema se encarrega dessas tarefas. A soluo de
C++ mais confivel do que a de C porque ela garante que a operao de desalocao
ser garantidamente chamada ao final do tempo de vida da lista.
Por outro lado, em termos de eficincia, C e C++ podem superar JAVA pois deixam a
cargo do programador a funo de desalocar memria quando esta no mais
necessria. Com isso, um bom programador pode explorar o contexto para dizer como
e em qual momento mais eficiente fazer a operao de desalocao.
12
13