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