Programação I/Introdução à Programação
Exame de 2ª época
1998/1999, 1º semestre
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 (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>
using namespace std;class A {
private:
int x[100];
int y;
int f1();public:
int z;
A(int a = 1, int b = 2, int c = 3) {
y = b;
...
}
int f2(int);
bool f3(int);
};int main() {
...
}
A b(1);que valor tem a variável y (variável membro da instância b da classe A) [apenas uma resposta correcta]?
A definição invoca o contrutor com um único argumento de valor 1, pelo que a variável membro y é inicializada com o valor por omissão do parâmetro b, que é 2.[cotação: 1,0]
A a[10];Quais das seguintes instruções estão correctas?
F int b = a.z[3];
A variável a é uma matriz, pelo que a utilização directa do operador de selecção de membro . não faz sentido.V int b = a[4].f2(2);
A variável a é uma matriz, sendo portanto a[4] uma expressão que se refere ao 5º elemento dessa matriz. Esse elemento é da classe A, pelo que é lícito invocar a função membro f2(), que tem de ser invocada com um argumento inteiro.F int b = a[3].x[2];
Neste caso o problema é que a variável membro x é privada, pelo que não pode ser usada directamente em código que não pertença a uma função ou procedimento membro de A (assume-se que o código existe em main())F int b = a.y;
Neste caso o problema é idêntico.[cotação: 2]
F int b = x;
A variável x é uma matriz, pelo que a sua atribuição a um inteiro não faz sentido.F int b = f2();
A função f2() exige um argumento inteiro, pelo que a sua invocação está errada.F int b = x[100];
A instrução está sintacticamente correcta, mas a matriz x tem apenas 100 elementos, pelo que é um erro tentar aceder ao elemento índice 100 (os índices válidos variam de 0 a 99 inclusivé).F int b = a.f1() + a.y;
Não existe qualquer variável de nome a disponível.[cotação: 2]
[cotação: 1]
PC: n > 0
CI: r = (S k : 0 < k < i : m1[k] m2[k]) e 0 < i <= n
CO: r = (S k : 0 <= k < n : m1[k] m2[k])
G: i <> n
[cotação: 1]
int produtoInterno(int m1[], int m2[], int n) {
int r = m1[0] * m2[0];
for(int i = 1; i != n; ++i)
r += m1[i] * m2[i];
return r;
}
[cotação: 2]
class Contacto {
public:
Contacto(const std::string& nome = "",
const std::string& telefone = "");
std::string nome() const;
std::string telefone() const;private:
std::string nome_;
std::string telefone_;
};bool operator == (const Contacto& a, const Contacto& b);
std::ostream& operator << (std::ostream& saída, const Contacto& c);
[cotação: 2]
class Contactos {
public:
Contactos();
void insereContacto(const Contacto& contacto);
void retiraContacto(const std::string& nome);
std::string telefone(const string& nome);private:
static const int limite = 100;
Contacto contactos[limite];
int número_de_contactos;
};
Contacto::Contacto(const std::string& nome,[cotação: 1]
const std::string& telefone)
: nome_(nome), telefone_(telefone) {
}
[cotação: 1]
bool operator == (const Contacto& a, const Contacto& b) {
return a.nome() == b.nome();
}
Contacto c("Manuel Maria", "1234567");resultem em aparecer no ecrã o seguinte texto:std::cout << c << std::endl;
O telefone do Manuel Maria é 1234567.[cotação: 1]
std::ostream& operator << (std::ostream& saída, const Contacto& c) {
return saída << "O telefone do " << c.nome() << " é " << c.telefone()
<< '.';
}
[cotação: 2]
std::string Contactos::telefone(const string& nome) {
for(int i = 0; i != número_de_contactos; ++i)
if(contactos[i].nome() == nome)
return contactos[i].telefone();
return "";
}
[cotação: 1]
int main()
{
Contactos c;for(int i = 0; i != 3; ++i) {
cout << "Introduza o nome: " << endl;
string n;
cin >> n;
cout << "Introduza o telefone: " << endl;
string t;
cin >> t;
c.insereContacto(Contacto(n, t));
}
}
[cotação: 2]Construtores: Deve existir um construtor por omissão (que cria um polígono degenerado com zero vértices) e um construtor que receba a sequência de vértices (na forma de uma matriz bidimensional com n × 2 elementos, sendo n dado por um segundo parâmetro).
Operador de igualdade (==): Dois polígonos são iguais se são constituídos pelos mesmos vértices pela mesma ordem. Tenha em atenção que a ordem dos vértices é importante mas que o primeiro vértice considerado na sequência é irrelevante (e.g., os triângulos ((1,1), (2,2), (3,3)) e ((1,1), (3,3), (2,2)) devem ser considerados diferentes e os triângulos ((1,1), (2,2), (3,3)) e ((2,2), (3,3), (1,1)) devem ser considerados iguais). Admita que os polígonos não contêm vértices repetidos.
void adicionaVértice(double x, double y): Adiciona o vértice (x, y) ao polígono.
void retiraVértice (double x, double y): Retira o primeiro vértice de coordenadas (x, y), caso exista. Se não existir não faz nada.
class Polígono {
public:
Polígono();
Polígono(double v[][2], int n);void adicionaVértice(double x, double y);
void retiraVértice(double x, double y);private:
friend bool operator == (const Polígono& a, const Polígono& b);
friend std::ostream& operator << (std::ostream& o, const Polígono& p);static const int limite = 20;
int número_de_vértices;
double vértices[limite][2];
};Polígono::Polígono(): número_de_vértices(0) {
}Polígono::Polígono(double v[][2], int n) : número_de_vértices(n) {
// Número de vértices não pode exceder o limite:
if(n > limite)
exit(1);for(int i = 0; i != n; ++i) {
vértices[i][0] = v[i][0];
vértices[i][1] = v[i][1];
}
}void Polígono::adicionaVértice(double x, double y) {
// Não se pode exceder o limite de vértices:
if(número_de_vértices == limite)
return;
vértices[número_de_vértices][0] = x;
vértices[número_de_vértices++][1] = y;
}void Polígono::retiraVértice(double x, double y) {
for(int i = 0; i != número_de_vértices; ++i)
if(vértices[i][0] == x && vértices[i][1] == y) {
// Retira vértice compactando matriz:
for(; i != número_de_vértices - 1; i++) {
vértices[i][0] = vértices[i + 1][0];
vértices[i][1] = vértices[i + 1][1];
}
--número_de_vértices;
}
}// Apresentam-se duas versões. A primeira admite que os vértices de uma polígono
// são todos diferentes, como sugerido no enunciado. A segunda é mais genérica.
bool operator == (const Polígono& a, const Polígono& b) {
if(a.número_de_vértices != b.número_de_vértices)
return false;int n = a.número_de_vértices;
// Se os polígonos não têm vértices então são iguais:
if(n == 0)
return true;// Procurar em b o vértice igual ao primeiro de a:
for(int i = 0; i != n; ++i)
if(a.vértices[0][0] == b.vértices[i][0] &&
a.vértices[0][1] == b.vértices[i][1]) {// Verificar se os restantes são iguais:
++i;
for(int j = 1; j != n; ++j, ++i) {
if(a.vértices[j][0] != b.vértices[i % n][0] ||
a.vértices[j][1] != b.vértices[i % n][1])
return false;// Se aqui se chega é porque são todos iguais:
return true;
}// Só se chega aqui se não se encontrou:
return false;
}bool operator == (const Polígono& a, const Polígono& b) {
if(a.número_de_vértices != b.número_de_vértices)
return false;int n = a.número_de_vértices;
// Se os polígonos não têm vértices então são iguais:
if(n == 0)
return true;for(int i = 0; i != n; ++i) {
// Verificar se são iguais começando em zero para a e em i para b:
bool iguais = true;
for(int j = 0; j != n && iguais; ++j)
if(a.vértices[j][0] != b.vértices[(i + j) % n][0] ||
a.vértices[j][1] != b.vértices[(i + j) % n][1])
iguais = false;
if(iguais)
return true;// Caso contrário procura-se mais à frente.
}// Só se chega aqui se não são iguais:
return false;
}// Este operador não era pedido no enunciado:
std::ostream& operator << (std::ostream& saída, const Polígono& p) {
saída << '(';
for(int i = 0; i != p.número_de_vértices; ++i) {
if(i != 0)
saída << ", ";
saída << '(' << p.vértices[i][0] << ", "
<< p.vértices[i][1] << ')';
}
return saída << ')';
}// Note-se que tudo se simplificaria se se definisse uma classe Coordenada para as coordenadas 2D.
[cotação: 1]
A diferença entre membros públicos e privados de uma classe está em quem tem direito de lhes aceder. Só têm acesso aos membros privados de uma classe outros membros ou amigos da mesma classe. A membros públicos pode-se aceder sem restrições. A vantagem principal da utilização de membros privados é a de esconder pormenores de implementação da classe de quem a vai usar. Para compreender bem esta vantagem é importante compreender que uma classe é implementada para posteriores utilizações, ou seja, para uma classe existe o "programador fabricante", que implementa a classe e se preocupa com a sua implementação, e o "programador utilizador", que usa a classe sem se preocupar com o "como ela faz", preocupando-se apenas com "o que ela faz". Claro está que este "programador utilizador " da classe pode estar por sua vez a fabricar outra classe, pelo que assume os dois papeis simultaneamente, tal como o marceneiro fabrica móveis usando um maço, que até pode ter sido ele a fabricar. À capacidade de olhar para uma entidade duma forma mais global sem preocupações com os pormenores chama-se capacidade de abstracção e é fundamental em qualquer programador.O esconder dos pormenores de implementação não é apenas uma vantagem para o "utilizador programador". O fabricante, ao ter a garantia de que o utilizador bem intencionado não acederá aos membros privados da classe (tipicamente variáveis membro), pode fazer assumpções acerca dos seus valores que não poderia de outra forma. Por exemplo, para a classe dos racionais falada nas folhas da cadeira, o programador da classe pode assumir que o denominador é positivo no início de cada função membro, o que lhe simplifica bastante a vida...
(Nota: a sua resposta poderia ser bem mais sucinta...)