Programação II /Programação Orientada por Objectos
Resolução do exame de 2ª época
1998/1999, 2º 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>class X {
public:
X();
...
};class Y {
public:
virtual void g() const = 0;
};class A : public Y {
int a;
X* px;public:
A(int aa) : a(aa), px(0) {
}
A(int aa, X* p) : a(aa), px(p) {
}void g() const {
std::cout << "Execução de A::g(). ";
}
};class B : public A {
int by;
public:
int bx;B(int a, int x, int y = 0) : A(a), by(y), bx(x) {
}
B(int a, int x, int y, X* p) : A(a, p), by(y), bx(x) {
}void g() const {
std::cout << "Execução de B::g(). ";
A::g();
}
};int main() {
...
}
F B* p; p->bx = 1;
As instruções estão correctas do ponto de vista sintáctico, mas o conteúdo apontado por p é usado na segunda instrução sem que o ponteiro p tenha sido inicializado.F B p(1, 2); p->bx = 1;
A variável p é do tipo B (e não ponteiro para B), pelo que o operador de selecção de membro a utilizar só poderia ser o operador ., e nunca o operador ->, que se usa apenas para acesso através de ponteiros ou para instâncias de classes que o definam explicitamente (não é o caso de B).F B p(1, 2); p.by = 1;
Neste caso o operador de selecção de membro está correcto, mas acontece que by é uma variável membro privada, pelo que estas instruções, que se presume estarem na função main(), não lhe podem aceder.F B& a; a.bx = 1;
A definição da referência a está errada: uma referência tem de ser sempre inicializada, pois introduz um sinónimo de qualquer coisa.[2 valores]Note-se que, no caso de um parâmetro duma função, a inicialização de uma referência é feita pelo respectivo argumento imediatamente antes da sua chamada, pelo que a inicialização não está explicita.
X* x = new X;quais das seguintes instruções são válidas?
A* a = new A(1);
B b(10, 20);
int* i = new int(11);
V B bb(b);
A linguagem encarrega-se de fornecer automaticamente um construtor por cópia para todas as classes onde tal seja possível. É o caso da classe B. Por isso é possível inicializar uma instância de B à custa de outra instância da mesma classe.F B bb(*i, *i, *i, &x);
No único construtor de B com quatro parâmetros o último é do tipo X*. Como x é do tipo X*, o seu endereço (optido pelo operador &) é do tipo X** (ponteiro para ponteiro para X), pelo que há uma incompatibilidade de tipos na invocação do construtor e portanto a definição está errada.F B bb;
A classe B não tem construtor por omissão. A linguagem só fornece um construtor por omissão automaticamente se o programador não fornecer explicitamente nenhum outro construtor (o que não é o caso).[1,5 valores]
A* a = new B(1, 2);o que aparece escrito no écrã (apenas uma resposta está correcta)?
a->g();
V
Execução de B::g(). Execução de A::g().
F
Execução de B::g().
F Não
aparece nada no ecrã.
O procedimento g() é declarado na classe Y (topo da hierarquia) como virtual. Assim, a invocação de g() através de um ponteiro para a classe A, mas que aponta um objecto da classe B, tem como resultado a invocação da especialização de g() em B (i.e., B::g()). Ou seja, ocorre polimorfismo.[1,5 valores]
class ItemDeBiblioteca {
public:
ItemDeBiblioteca(const std::string& título) : título_(título) {
}
virtual ~ItemDeBiblioteca() {
}// Função membro que devolve true caso o item de biblioteca aborde o assunto passado como
// argumento e false no caso contrário.
virtual bool aborda(const std::string& assunto) const = 0;std::string título() const {
return título_;
}private:
std::string título_;
};
Não é necessário implementar qualquer um dos métodos nesta alínea.
[1 valor]
class Artigo {
public:
Artigo(const std::string& autor, const std::string& título,
const std::string& assunto);
Artigo();std::string autor() const;
std::string título() const;
std::string assunto() const;private:
std::string autor_;
std::string título_;
std::string assunto_;
};
[2 valores]
class Revista : public ItemDeBiblioteca {
public:
Revista(const std::string& título, const std::string& editora);std::string editora() const;
bool aborda(const std::string& assunto) const;
void acrescenta(const Artigo& artigo);
private:
std::string editora_;
Lista<Artigo> artigos;
};
[0,5 valor]
inline Revista::Revista(const std::string& título,
const std::string& editora)
: ItemDeBiblioteca(título), editora_(editora) {
}
bool Revista::aborda(const std::string& assunto) const {[2 valores]
for(Lista<Artigo>::IteradorConstante i = artigos.primeiro();
i != artigos.fim(); ++i)
if(i.item().assunto() == assunto)
return true;
return false;
}
// Apresenta-se aqui a implementação completa incluindo um programa de teste:#include <string>
#include <iostream>#include "lista.H"
class ItemDeBiblioteca {
public:
ItemDeBiblioteca(const std::string título);
virtual ~ItemDeBiblioteca();// Função membro que devolve true caso o item de biblioteca aborde
// o assunto passado como argumento e false no caso contrário.
virtual bool aborda(const std::string& assunto) const = 0;std::string título() const;
private:
std::string título_;
};inline ItemDeBiblioteca::ItemDeBiblioteca(const std::string título)
: título_(título) {
}inline ItemDeBiblioteca::~ItemDeBiblioteca() {
}inline std::string ItemDeBiblioteca::título() const {
return título_;
}class Artigo {
public:
Artigo(const std::string& autor, const std::string& título,
const std::string& assunto);
Artigo();std::string autor() const;
std::string título() const;
std::string assunto() const;private:
std::string autor_;
std::string título_;
std::string assunto_;
};Artigo::Artigo(const std::string& autor, const std::string& título,
const std::string& assunto)
: autor_(autor), título_(título), assunto_(assunto) {
}Artigo::Artigo() {
}inline std::string Artigo::autor() const {
return autor_;
}inline std::string Artigo::título() const {
return título_;
}inline std::string Artigo::assunto() const {
return assunto_;
}class Revista : public ItemDeBiblioteca {
public:
Revista(const std::string& título, const std::string& editora);std::string editora() const;
bool aborda(const std::string& assunto) const;
void acrescenta(const Artigo& artigo);
private:
std::string editora_;
Lista<Artigo> artigos;
};inline Revista::Revista(const std::string& título,
const std::string& editora)
: ItemDeBiblioteca(título), editora_(editora) {
}inline std::string Revista::editora() const {
return editora_;
}// Não é inline! Colocar no ficheiro de implementação (.C ou .cpp).
bool Revista::aborda(const std::string& assunto) const {
for(Lista<Artigo>::IteradorConstante i = artigos.primeiro();
i != artigos.fim(); ++i)
if(i.item().assunto() == assunto)
return true;
return false;
}inline void Revista::acrescenta(const Artigo& artigo) {
artigos.poeFim(artigo);
}int main() {
Revista r("IEEE Computer", "IEEE Press");
r.acrescenta(Artigo("M. M. Sequeira", "How to avoid programming",
"programming"));
r.acrescenta(Artigo("L. M. Nunes", "How to avoid typing",
"typing"));while(true) {
std::cout << "Que assunto? ";
std::string assunto;
std::getline(std::cin, assunto);
if(assunto == "")
break;
std::cout << "O assunto '" << assunto << "' "
<< (r.aborda(assunto) ? "" : "não ")
<< "é abordado na revista" << std::endl;
}
}
void rodaÀDireita(int m[], int n) {[1 valor]
int aux = m[n - 1];
for(int i = n - 1; i != 0; --i)
m[i] = m[i - 1];
m[0] = aux;
}
void ordenaPorSelecção(int m[], int n) {[2 valores]
// Ver Michael Main e Walter Savitch, "Data Structures and Other Objects Using C++",
// Addison-Wesley, Reading Massachusetts, 1997, secção 13.1.
}
Defina completamente um modelo de estrutura chamada Nó que represente um nó de uma lista duplamente ligada circular que pode guardar um item de tipo parametrizável (inclua um construtor).
[0,5 valores]
template <typename I>
struct Nó {
Nó* anterior;
Nó* seguinte;
I item;
Nó(Nó* a, Nó* s, const I& item = I());
};template <typename I>
inline Nó<I>::Nó(Nó* a, Nó* s, const I& i)
: anterior(a), seguinte(s), item(i) {
}
template <typename I>[1,5 valores]
void põeNoInício(Nó<I>*& primeiro, const I& i) {
if(primeiro == 0) {
primeiro = new Nó<I>(0, 0, i);
primeiro->anterior = primeiro;
primeiro->seguinte = primeiro;
} else {
primeiro = new Nó<I>(primeiro->anterior, primeiro, i);
primeiro->anterior->seguinte = primeiro;
primeiro->seguinte->anterior = primeiro;
}
}
template <typename I>[2 valores]
void remove(Nó<I>*& primeiro, Nó<I>* nó) {
if(primeiro->anterior == primeiro) {
// Lista com um único nó (primeiro == nó):
delete primeiro;
primeiro = 0;
} else {
nó->anterior->seguinte = nó->seguinte;
nó->seguinte->anterior = nó->anterior;
if(nó == primeiro)
// Se se estiver a remover o primeiro:
primeiro = nó->seguinte;
delete nó;
}
}
template <typename I>[1,5 valores]
void rodaÀDireita(Nó<I>*& primeiro) {
if(primeiro != 0)
primeiro = primeiro->anterior;
}
// Apresenta-se aqui a implementação completa incluindo um programa de teste:template <typename I>
struct Nó {
Nó* anterior;
Nó* seguinte;
I item;
Nó(Nó* a, Nó* s, const I& item = I());
};template <typename I>
inline Nó<I>::Nó(Nó* a, Nó* s, const I& i)
: anterior(a), seguinte(s), item(i) {
}template <typename I>
void põeNoInício(No<I>*& primeiro, const I& i) {
if(primeiro == 0) {
primeiro = new Nó<I>(0, 0, i);
primeiro->anterior = primeiro;
primeiro->seguinte = primeiro;
} else {
primeiro = new Nó<I>(primeiro->anterior, primeiro, i);
primeiro->anterior->seguinte = primeiro;
primeiro->seguinte->anterior = primeiro;
}
}template <typename I>
void remove(Nó<I>*& primeiro, Nó<I>* nó) {
if(primeiro->anterior == primeiro) {
// Lista com um único nó (primeiro == nó):
delete primeiro;
primeiro = 0;
} else {
nó->anterior->seguinte = nó->seguinte;
nó->seguinte->anterior = nó->anterior;
if(nó == primeiro)
// Se se estiver a remover o primeiro:
primeiro = nó->seguinte;
delete nó;
}
}template <typename I>
void rodaÀDireita(Nó<I>*& primeiro) {
if(primeiro != 0)
primeiro = primeiro->anterior;
}#include <iostream>
template <typename I>
void mostra(const Nó<I>* primeiro) {
if(primeiro == 0)
std::cout << "Vazia!" << std::endl;
else {
for(const Nó<I>* nó = primeiro; nó != primeiro->anterior;
nó = nó->seguinte)
std::cout << nó->item << ' ';
std::cout << primeiro->anterior->item << std::endl;
}
}int main() {
Nó<int>* primeiro = 0;
põeNoInício(primeiro, 1);
remove(primeiro, primeiro);
mostra(primeiro);põeNoInício(primeiro, 4);
põeNoInício(primeiro, 3);
põeNoInício(primeiro, 2);
põeNoInício(primeiro, 1);
mostra(primeiro);
remove(primeiro, primeiro->seguinte);
mostra(primeiro);
remove(primeiro, primeiro->anterior);
mostra(primeiro);
remove(primeiro, primeiro);
mostra(primeiro);
remove(primeiro, primeiro);
mostra(primeiro);põeNoInício(primeiro, 4);
põeNoInício(primeiro, 3);
põeNoInício(primeiro, 2);
põeNoInício(primeiro, 1);
rodaÀDireita(primeiro);
mostra(primeiro);
}
[1 valor]
Ver aula prática 5, Secção 1.3.