Resolução da frequência
Introdução à Programação
IGE e ETI
1º semestre de 2000/2001
ISCTE
Deve preencher todos os espaços indicados por um sublinhado (___) com V ou F. Qualquer espaço não preenchido será considerado como uma resposta errada.
Em geral as alíneas podem ter zero ou mais respostas correctas. Cada resposta correctamente assinalada vale 0,5 valores.
Nas alíneas em que apenas uma resposta está correcta (se existirem estão assinaladas no texto), responder com mais ou menos do que um V anula a cotação. A resposta correcta corresponde à cotação completa. Qualquer outra resposta corresponde a zero valores.
Em todos os casos em que não é explicitamente referida a localização de uma instrução, considere que esta é dada na função main() do programa seguinte:
#include <iostream>
#include <vector>
using namespace std;
class A {
public:
int const m;
A();
A(int const a, int const b = 0);
int p() const;
vector<int>::size_type t() const;
void a(int const n);
void r(int const i);
private:
vector<int> v;
};
A::A(int const a, int const b) : m(30), v(a, b) {
}
vector<int>::size_type A::t() const {
return v.size();
}
void A::a(int const n) {
v.push_back(n);
}
...
int main()
{
int i = 11, j = 12;
A um_a(23, i);
A const outro_a(18, j);
...
}
1.1 Admita que qualquer uma destas instruções é
dada na função main()
imediatamente após a definição da constante
outro_a
. Quais das seguintes instruções estão
correctas?
V A a(um_a);
V A a;
V A
a(outro_a.m);
1.2 Admita que qualquer uma destas instruções é
dada na função main()
do programa acima. Quais das seguintes
instruções estão correctas?
F um_a.m = 1;
m
é uma constante membro da classeA
.
V um_a.r(1);
F
outro_a.m = 1;
m
é uma constante membro da classeA
eoutro_a
é uma instância constante da classeA
.
V int m = outro_a.p();
[cotação: 2]
1.3 Assuma que as seguintes instruções são dadas
dentro de uma função ou procedimento membro da classe A
e que não
são declaradas quaisquer variáveis ou constantes dentro dessa função ou
procedimento (que não tem quaisquer parâmetros). Quais das seguintes
instruções estão correctas?
F A.m = 1;
Não existe qualquer instância (variável) de nome
A.
F v =
1;
[cotação: 1]Não se podem atribuir valores literais inteiros a vectores.
1.4 Suponha o seguinte código adicional:
Assumindo que os restantes métodos da classe estão definidos, qual é o resultado da invocação do programa?
void fazQualquerCoisa(A a, int& x)
{
x += a.m;
a.a(x);
}
int main()
{
A a(19);
int x = 7;
fazQualquerCoisa(a, x);
cout << a.t() << " " << x << endl;
}
F 20 7
F 20 37
F 19 7
V 19 37
A construção de
a
é feita pelo segundo construtor. A constante membro inteiram
é inicializada com 30 e o vector de inteirosv
com 19 itens todos com valor zero (0). Em seguida, a variávelx
é construída com o valor 7. Quando o procedimentofazQualquerCoisa()
é invocado, o argumentoa
, passado por valor, é copiado para o parâmetroa
ex
, passado por referência, é usado para inicializar o parâmetro referênciax
, que se torna um sinónimo da variável do mesmo nome na funçãomain()
. O procedimento altera o valor dex
acrescentando-lhe o valor guardado na constantem
membro dea
. Assim, a variávelx
na funçãomain()
passa a tomar o valor 37. Depois é invocado o procedimento membroa()
que acrescenta o valor dex
ao vectorv
membro dea
e termina a chamada do procedimentoazQualquerCoisa()
. Comoa
é passada por valor, as alterações realizadas dentro do procedimento perdem-se, passando-se exactamente o contrário comx
que foi passado por referência. Assim, a invocação da função membrot()
devolve o valor original (antes da chamada defazQualquerCoisa()
) do tamanho do vectorv
, isto é, 19 ex
contém o valor 37. Ambos são inseridos no canal de saídacout
.
[cotação: 0,5]
Questão 2
Actualmente, existem em Portugal várias lojas especializadas na importação de banda desenhada. Estas lojas, usualmente, têm uma carteira de clientes que realiza uma série de encomendas. Estas encomendas (que podem ser realizadas pela Internet) são constituídas pela data (cadeia de caracteres), o título (cadeia de caracteres), a editora (cadeia de caracteres), quantidade de exemplares (inteiro) e o preço por unidade (vírgula flutuante).
Uma destas lojas pensou que usando um sistema informático seria mais fácil satisfazer as necessidades dos clientes.
2.1 Defina apenas a interface de uma classe
Encomenda
que representa uma encomenda realizada por um cliente da
loja. A classe Encomenda
deve dispor de um construtor que
recebe os dados necessários à construção de uma encomenda e funções membro que
permitam inspeccionar:
Indique claramente quais dos métodos da classe não alteram a instância implícita durante a sua invocação.
Não é necessário nesta alínea definir quaisquer funções ou procedimentos membro da classe.
[cotação: 3]
2.2 A classe ContaDeCliente
representa
a conta de um cliente na loja e é definida do seguinte modo:
class ContaDeCliente {
public:
ContaDeCliente(string const& nome,
string const& morada,
string const& endereço_de_correio_electrónico);
string nome() const; //
Devolve o nome do cliente.Devolve a morada do cliente.
string morada() const; //Devolve o endereço de correio
string endereçoDeCorreioElectrónico() const; //electrónico do cliente.
//Adiciona a encomenda
void adiciona(Encomenda const& encomenda); //à conta do cliente.
// encomendaMostra no ecrã todas as
void mostraEncomendas() const; //encomendas que estão na
//na conta do cliente.
//Devolve o valor total das
double valorEmDívida() const; //encomendas do cliente.
//
private:
string nome;
string morada;
string endereço_de_correio_electrónico;
vector<Encomenda> encomendas;
};
Defina os métodos:
void adiciona(Encomenda const&
encomenda)
e
double valorEmDívida() const
. da classe ContaDeCliente
.
[cotação: 3.5]
R:
#include <iostream>
Devolve o nome do cliente.
#include <string>
#include <vector>
using namespace std;
class Encomenda {
public:
Encomenda(string const& data,
string const& titulo,
string const& editora,
int const quantidade_de_exemplares,
double const preco_por_unidade);
string data() const;
string titulo() const;
string editora() const;
int quantidadeDeExemplares() const;
double precoPorUnidade() const;
double precoTotal() const;
private:
string data_;
string titulo_;
string editora_;
int quantidade_de_exemplares;
double preco_por_unidade;
};
Encomenda::Encomenda(string const& data,
string const& titulo,
string const& editora,
int const quantidade_de_exemplares,
double const preco_por_unidade)
: data_(data), titulo_(titulo), editora_(editora),
quantidade_de_exemplares(quantidade_de_exemplares),
preco_por_unidade(preco_por_unidade)
{}
inline string Encomenda::data() const
{
return data_;
}
inline string Encomenda::titulo() const
{
return titulo_;
}
inline string Encomenda::editora() const
{
return editora_;
}
inline int Encomenda::quantidadeDeExemplares() const
{
return quantidade_de_exemplares;
}
inline double Encomenda::precoPorUnidade() const
{
return preco_por_unidade;
}
inline double Encomenda::precoTotal() const
{
return quantidade_de_exemplares * preco_por_unidade;
}
class ContaDeCliente {
public:
ContaDeCliente(string const& nome,
string const& morada,
string const& endereco_de_correio_electronico);
string nome() const; //Devolve a morada do cliente.
string morada() const; //Devolve o endereço de correio
string enderecoDeCorreioElectronico() const; //electrónico do cliente.
//Adiciona a encomenda
void adiciona(Encomenda const& encomenda); //encomenda à conta do cliente.
//Mostra no ecrã todas as
void mostraEncomendas() const; //encomendas que estão na
//na conta do cliente.
//Devolve o valor total das
double valorEmDivida() const; //encomendas do cliente.
//Programa demonstrativo do uso das classes
private:
string nome_;
string morada_;
string endereco_de_correio_electronico;
vector<Encomenda> encomendas;
};
ContaDeCliente::ContaDeCliente(string const& nome,
string const& morada,
string const& endereco_de_correio_electronico)
: nome_(nome), morada_(morada),
endereco_de_correio_electronico(endereco_de_correio_electronico)
{}
inline string ContaDeCliente::nome() const
{
return nome_;
}
inline string ContaDeCliente::morada() const
{
return morada_;
}
inline string ContaDeCliente::enderecoDeCorreioElectronico() const
{
return endereco_de_correio_electronico;
}
inline void ContaDeCliente::adiciona(Encomenda const& encomenda)
{
encomendas.push_back(encomenda);
}
void ContaDeCliente::mostraEncomendas() const
{
for(vector<Encomenda>::size_type i = 0;
i != encomendas.size();
++i)
cout << encomendas[i].data() << " "
<< encomendas[i].titulo() << " "
<< encomendas[i].editora() << " "
<< encomendas[i].quantidadeDeExemplares() << " "
<< encomendas[i].precoPorUnidade() << " ... "
<< encomendas[i].precoTotal() << endl;
}
double ContaDeCliente::valorEmDivida() const
{
double valor_em_divida = 0.0;
for(vector<Encomenda>::size_type i = 0;
i != encomendas.size();
++i)
valor_em_divida += encomendas[i].precoTotal();
return valor_em_divida;
}
//Encomenda
eContaDeCliente
.
int main()
{
ContaDeCliente c("Jaquim", "Lisboa", "jaquim@dominio.pt");
cout << "Quantas encomendas deseja fazer hoje?" << endl
<< "--> ";
int n;
cin >> n;
for(int i = 0; i != n; ++i) {
cout << "Título: ";
string titulo;
cin >> titulo;
cout << "Editora: ";
string editora;
cin >> editora;
cout << "Quantidade: ";
int quantidade;
cin >> quantidade;
c.adiciona(Encomenda("2001/2/22", titulo, editora, quantidade, 1500));
// Promoção: tudo a 1 500$.
}
cout << endl
<< "Cliente: " << c.nome() << endl;
cout << "--------------------------------------------------" << endl;
c.mostraEncomendas();
cout << "--------------------------------------------------" << endl;
cout << "Total em dívida: " << c.valorEmDivida() << endl;
}
Questão 3
Quando se edita um programa usando o Emacs
, pode-se
utilizar o comando C-k
(por exemplo) para cortar texto e o comando
C-y
para voltar a colá-lo onde for mais conveniente. É
possível também colar cortes anteriores ao último usando o comando
M-y
. Após uma colagem com o comando C-y
, dar o comando M-y
substitui o corte que
acabou de ser colado pelo corte anterior na sequência de cortes realizados desde
o início da sessão. Quando este comando é repetido sobre a colagem do
primeiro dos cortes realizados na sessão, essa colagem é substituída pelo último
corte realizado. Por isso no Emacs
não se fala em sequências de
corte, mas sim em anéis de corte.
Construa a classe AnelDeCortes que representa o conceito de anel de cortes atrás descrito, permitindo deste modo:
A classe AnelDeCortes
pode-se usar como se
segue:
AnelDeCortes anel;
anel.guarda("Primeiro corte");
anel.guarda("Segundo corte");
cout << anel.corteActual() << endl; // Aparece no monitor 'Segundo corte'.
anel.anterior();
cout << anel.corteActual() << endl; // Aparece no monitor 'Primeiro corte'.
anel.guarda("Terceiro corte");
cout << anel.corteActual() << endl; // Aparece no monitor 'Terceiro corte'.
anel.anterior();
cout << anel.corteActual() << endl; // Aparece no monitor 'Segundo corte'.
anel.anterior();
cout << anel.corteActual() << endl; // Aparece no monitor 'Primeiro corte'.
anel.anterior();
cout << anel.corteActual() << endl; // Aparece no monitor 'Terceiro corte'.
Indique claramente quais dos métodos da classe não alteram a instância implícita.
Defina completamente a classe, incluindo a definição de todos os seus métodos.
[cotação: 3,5]
R:
#include <iostream>
Aparece no monitor 'Segundo corte'.
#include <cassert>
#include <string>
#include <vector>
using namespace std;
class AnelDeCortes {
public:
void guarda(string const& corte);
void anterior();
string corteActual() const;
private:
vector<string> cortes;
int corte_actual;
};
void AnelDeCortes::guarda(string const& corte)
{
cortes.push_back(corte);
corte_actual = cortes.size() - 1;
}
void AnelDeCortes::anterior()
{
assert(cortes.size() != 0);
if(corte_actual == 0)
corte_actual = cortes.size() - 1;
else
--corte_actual;
}
string AnelDeCortes::corteActual() const
{
assert(cortes.size() != 0);
return cortes[corte_actual];
}
int main()
{
AnelDeCortes anel;
anel.guarda("Primeiro corte");
anel.guarda("Segundo corte");
cout << anel.corteActual() << endl; //Aparece no monitor 'Primeiro corte'.
anel.anterior();
cout << anel.corteActual() << endl; //Aparece no monitor 'Terceiro corte'.
anel.guarda("Terceiro corte");
cout << anel.corteActual() << endl; //Aparece no monitor 'Segundo corte'.
anel.anterior();
cout << anel.corteActual() << endl; //Aparece no monitor 'Primeiro corte'.
anel.anterior();
cout << anel.corteActual() << endl; //Aparece no monitor 'Terceiro corte'.
anel.anterior();
cout << anel.corteActual() << endl; //
}
Questão 4
Suponha a função int produto(int const m[], int const
n)
que devolve o produto dos n
elementos de uma matriz
m
de int
:
4.1 Indique a pré-condição (PC) e a condição objectivo (CO) da função e ainda a condição invariante (CI) do seu ciclo interno.
int produto(int const m[], int const n)
{
int produto = 1;
int i = 0;
while(i != n) {
produto *= m[i];
++i;
}
return produto;
}
[cotação: 1,5]
R:
PC: n >= 0.
CO: produto = (P j : 0 <= j < n : m[j]).
CI: produto = (P j : 0 <= j < i : m[j]) e 0 <= i <= n.
4.2 Prove que o ciclo está correcto verificando que:
//
PC //
implica CI.
// PC: n >= 0.
int produto = 1;
int i = 0;
// CI: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i <= n, ou seja,
// CI: 1 = (P j
: 0 <= j < 0 : m[j]) e 0 <= 0 <= n,
ou seja,
// CI: 1 = 1 e
0 <= n, ou seja, dada a PC,
// CI: V.
//
G e CI //
implica CI
// CI
e G: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i <= n e i <> n,
ou seja,
// CI e G: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i < n.
produto
*= m[i];
++i;
// implica CI: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i <= n
A demonstração pode ser feita duma forma directa, deduzindo as asserções verdadeiras após cada instrução:
// CI
e G: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i < n.
produto
*= m[i];
// produto
= (P j : 0 <= j < i : m[j])
× m[i] e 0 <= i < n, ou
seja,
// produto
= (P j : 0 <= j < i + 1 : m[j])
e 0 <= i < n.
++i;
// produto
= (P j : 0 <= j < (i
- 1) + 1 : m[j]) e 0 <= i - 1 < n, ou seja,
// produto
= (P j : 0 <= j < i : m[j])
e 1 <= i < n + 1, ou seja,
// produto
= (P j : 0 <= j < i : m[j])
e 1 <= i <= n,
// que é exactamente CI.
CI e ¬G: produto
= (P j : 0 <= j < i : m[j])
e 0 <= i <= n e i = n,
ou seja,
produto
= (P j : 0 <= j < n : m[j])
e 0 <= n <= n e i = n,
ou seja,
produto
= (P j : 0 <= j < n : m[j])
e V e i = n, que implica
CO: produto
= (P j : 0 <= j < n : m[j])
Questão 5
Explique claramente as vantagens de conhecer e usar regularmente um programa depurador de erros durante o desenvolvimento de um programa.
[cotação: 2]
R:
As vantagens de conhecer e usar regularmente um programa depurador de erros advêm da sua funcionalidade básica, que é a de permitir efectuar o traçado de um programa. Isto é, um depurador permite executar o código, instrução por instrução, inspeccionar o valor das variáveis durante a execução do programa, subir e descer na pilha de invocações de funções e procedimentos, definir pontos de paragem na execução de um programa, entre outras capacidades. Todas estas características tornam este tipo de aplicações numa ferramenta indispensável na correcção de erros lógicos (que ocorrem durante a execução do programa) de uma forma rápida e eficiente.
[Embora seja conveniente olhar com atenção para o código fonte do programa antes de usar o depurador, muitas vezes essa observação não conduz a progressos, pois os olhos do programador recusam-se a identificar os seus próprios erro. Nessa altura é necessário fazer o traçado do programa e verificar onde ocorre o erro.
Fazer um traçado "no papel" é útil, mas é comum que se cometam erros no próprio traçado. É desejável, por isso, observar os valores das variáveis enquanto o programa executa de facto: para isso um depurador é crucial. Uma alternativa muito fraca, embora infelizmente muito usada, à utilização de um depurador é a alteração do código colocando instruções de inserção no canal de saída (
cout
) que permitem ver o valor de variáveis. Mas este método tem vários problemas:
- Altera o código, o que lhe pode introduzir erros adicionais.
- Nem sempre há uma consola disponível para escrever os valores.
- Sempre que se pretende ver o valor de uma outra variável num outro ponto do programa, é necessário alterar de novo o código, compilar e executar.
- É extremamente rígido.
]