Nome do aluno: _______________________________________________
Número do aluno: ______________________
Assinatura do docente: ________________________________________
Identificação
Nome do aluno: _______________________________________________Número do aluno: ______________________
Turma: ____________
Exame de 2 ª época
Programação Orientada para Objectos
IGE e ETI
2º semestre de 1999/2000
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.
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 <cassert>
using namespace std;class Vector {
public:
typedef int Valor;Vector(int tamanho);
virtual ~Vector() { ... }
int tamanho() const { return tamanho_; }
virtual Valor const& operator [] (int i) const = 0;
virtual Valor& operator [] (int i) = 0;protected:
virtual Valor& acede(int i) { return dados[i]; }private:
int tamanho_;
Valor* dados;
};inline Vector::Valor const& Vector::operator [] (int i) const {
return dados[i];
}Vector::Vector(int tamanho) : tamanho_(tamanho), dados(new Valor[tamanho]) {
assert(tamanho >= 0);
for(int i = 0; i != tamanho; ++i)
dados[i] = Valor();
}class VectorVerificado : public Vector {
public:
VectorVerificado(int tamanho) : Vector(tamanho) {}
virtual Valor const& operator [] (int i) const {
assert(0 <= i && i < tamanho());
return Vector::operator [] (i);
}
virtual Valor& operator [] (int i) {
assert(0 <= i && i < tamanho());
return acede(i);
}
};int main() {
...
}
__ free(dados);
__ delete[tamanho] dados;
__ delete dados;
__ delete dados[];
[2 valores]
VectorVerificado* vv = new VectorVerificado(3);qual o resultado produzido pelo programa quando executado (apenas uma das respostas está correcta)?
Vector* v = vv;
vv[0] = 1;
vv[1] = 2;
vv[2] = 3;
cout << v[3];
delete v;
__ Com sorte o programa aborta e com azar
escreve lixo no ecrã.
__ O programa aborta por uma das asserções
falhar.
__ O programa escreve 3 no ecrã.
[0,5 valores]
__ Vector* v; v = new Vector(4);
__ Vector* v; v = new VectorVerificado(3); cout <<
v->acede(1);
[1 valor]
class A {Suponha que a classe Vector começa com:
public:
A(int v) : valor(v) {}
friend ostream& operator << (ostream&, A const&);
private:
int valor = 1;
};inline ostream& operator << (ostream& saída, A const& a) {
return saída << a.valor;
}
class Vector {Que sucede quando se tenta compilar e executar o seguinte programa?
public:
typedef A Valor;
int main() {__ Surge no ecrã 1.
VectorVerificado v(3);
cout << v[0] << endl;
}
[1,5 valores]
A classe deve ser preparada para poder ser derivada sem problemas.
A classe deve fornecer funções de inspecção para o nome, sexo, pai, mãe e filhos da pessoa.
Deve também fornecer um procedimento membro void mostra() const que escreve no ecrã o nome e sexo da pessoa.
Finalmente, deve fornecer as seguintes funções de verificação de grau de parentesco:
bool éDescendenteDe(Pessoa const* ascendente) const;Deve possuir um construtor que serve não apenas para inicializar o nome e sexo de uma nova pessoa, mas também para indicar quem são os seus pais (afinal, construir uma pessoa é concebê-la). Os parâmetros usados para indicar os pais da nova pessoa devem ter valor por omissão 0. A utilização de 0 no valor dos ponteiros para um qualquer dos pais significa que ele não é conhecido ou relevante para o problema em causa.
bool éAscendenteDe(Pessoa const* descendente) const;
bool éParenteDe(Pessoa const* pessoa) const;
Assuma definido o tipo enumerado Sexo:
enum Sexo {masculino, feminino};Tenha o cuidado de indicar claramente os métodos que não alteram a instância implícita.
Nesta alínea não é necessário definir nenhum dos métodos (funções ou procedimentos membro) da classe.
[1 valor]
O construtor serve não apenas para inicializar o nome e sexo de uma nova pessoa, mas também para indicar quem são os seus pais. Os parâmetros usados para indicar os pais da nova pessoa devem ter valor por omissão 0. A utilização de 0 no valor dos ponteiros para um qualquer dos pais significa que ele não é conhecido ou relevante para o problema em causa. Por cada pai especificado (não 0), deve ser verificado o respectivo sexo. O programa aborta se os sexos estiverem errados.
É muito importante que o construtor da nova pessoa registe essa mesma pessoa como filha de cada um dos pais especificados!
[1 valor]
Defina também a função bool éAscendenteDe(Pessoa const* descendente) const. Recorde-se que A é ascendente de B se B for descendente de A. Recorra a essa informação para escrever esta função à custa de uma simples chamada da função anterior.
Tenha cuidado porque as pessoas em causa podem não ter algum (ou nenhum) dos pais especificados. Nesse caso considere que a pessoa foi concebida por partenogénese ou "geração espontânea" :-)
[2 valores]
class Coisa {Defina totalmente uma classe Prenda para representar prendas. Uma prenda é uma coisa. Além do nome, as prendas têm um valor pecuniário e um valor simbólico (em unidade indefinidas, mas representadas por double). Além disso, as prendas têm associada uma pessoa que as ofereceu (ponteiro para a classe Pessoa). A classe, para além do construtor (que inicializa o nome, dador e valores de uma nova prenda), deve possuir inspectores para os dois valores e um procedimento mostra() que, para além de mostrar o nome da prenda, indique os seus dois valores da prenda e o nome da pessoa que a deu.
public:
Coisa(std::string const& nome)
: nome_(nome) {
}
virtual ~Coisa() {}
std::string const& nome() const;
return nome_;
}
virtual void mostra() const {
std::cout << nome() << std::endl;
}
private:
std::string nome_;
};
Tenha o cuidado de indicar claramente os métodos que não alteram a instância implícita.
[1 valor]
A classe Festa representa um jantar-festa. Cada festa tem um conjunto de participantes, que podem ser divididos em anfitriões e convidados. Cada festa tem também um nome (e.g., "Anos do Manel"), um número de convidados objectivo (e.g., 20) e um custo (em unidade indefinidas, mas representadas por double). Quando uma festa é construída, ainda não tem quaisquer anfitriões ou convidados.
Deve ser possível realizar as seguintes operações com uma festa:
A classe Festa deve guardar as listas de:
A classe abstracta Participante representa um participante na festa. Um participante é uma pessoa. Além disso, cada participante deve saber a festa em que participa (ponteiro para a festa) e se está ou não sentado à mesa. Deve ser possível realizar as seguintes operações com um participante:
Quando se constrói um anfitrião, este introduz-se imediatamente (e automaticamente) na festa, invocando no seu construtor o método da festa que introduz um anfitrião.
Um convidado é um tipo específico de participante na festa. Os convidados, para além daquilo que qualquer participante guarda, guardam um ponteiro para uma prenda a entregar na festa. Essa prenda é construída (comprada...) quando o convidado é construído. Quando se ordena a um convidado que se sente, este só o faz se todos os anfitriãos da festa estiverem já sentados. Deve ser possível realizar as seguintes operações adicionais com um convidado:
Tenha o cuidado de indicar claramente os métodos que não alteram a instância implícita em todas as classes.
Nesta alínea não é necessário definir nenhum dos métodos (funções ou procedimentos membro) da classe.
[3 valores]
[1,5 valores]
struct Elo {usada para representar os elos de uma cadeia simplesmente ligada com a particularidade de ser circular, i.e., o elo seguinte do último elo da cadeia é o primeiro elo da cadeia. A cadeia não tem guardas.
typedef int Item;Item item;
Elo* seguinte;
Elo(Item const& item, Elo* seguinte = 0) : item(item), seguinte(seguinte) {
}
};
Defina um procedimento void inverte(Elo*& último) que inverta a ordem dos elos de uma cadeia simplesmente ligada, circular e sem guardas cujo último elo é apontado pelo ponteiro último. Note que quando a cadeia está vazia o ponteiro para o último elemento tem o valor 0. Note também que em geral o último elo da cadeia passa a ser o que antes era o primeiro.
Faça diagramas da cadeia em várias situações! Só assim resolverá com sucesso esta alínea!
[2 valores]
#include <iostream>[2 valores]using namespace std;
// Procedimento que descarta todos os caracteres lidos do teclado até ao primeiro
// fim-de-linha encontrado:
void ignora() {
char c;
while(cin.get(c) && c != '\n')
;
}class Vector {
public:
Vector() : x_(0.0), y_(0.0), z_(0.0) {}
Vector(double x, double y, double z) : x_(x), y_(y), z_(z) {}double x() const { return x_; }
double y() const { return y_; }
double z() const { return z_; }Vector& operator += (Vector const& v) {
x_ += v.x_; y_ += v.y_; z_ += v.z_;
return *this;
}class ErroAoCarregar {};
// Carrega as coordenadas do vector a partir do canal entrada. Em caso de
// erro lança a excepção ErroAoCarregar e mantém o valor original do vector
// intacto!
void carrega(istream& entrada);// Lê do teclado as coordenadas. Se tudo correr bem devolve true. Se a
// leitura falhar devolve false e mantém o valor original do vector intacto!
bool le();// Lê do teclado as coordenadas, depois de as pedir. Em caso de erro
// avisa o utilizador e pede para ele introduzir de novo ignorando toda a
// linha. Só termina depois de lidas com sucesso as três coordenadas.
void leInteractivo();private:
double x_, y_, z_;
};Vector operator + (Vector a, Vector const& b) {
return a += b;
}void Vector::carrega(istream& entrada) {
// Complete aqui:
}
bool Vector::le() {
// Complete aqui:
}
void Vector::leInteractivo() {
// Complete aqui:
}
[1 valor]