Programação II /Programação Orientada por Objectos
Resolução da frequência
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 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 A {
int a;
public:
A(int aa) : a(aa) {}
void g() {
std::cout << "Execução de A::g(). ";
}
};class B : public A {
public:
int bx;
B(int, int); // Não há construtor por omissão.
void g() {
A::g();
std::cout << "Execução de B::g(). ";
}
};inline B::B(int aa, int bb) ...
int main() {
...
}
F B* p = new B; p->bx = 1;
A primeira instrução está errada, pois a classe B não tem construtor por omissão. Assim, deveriam ter sido passados argumentos ao construtor, como acontece na sequência de instruções seguinte:F B b(1, 2); b->bx = 1;
A segunda instrução está errada. O operador de selecção de membro -> só se pode aplicar se o operando esquerdo for um ponteiro. Mas b é uma instância da classe B, e não um ponteiro para B.F B* p; B->bx = 1;
Neste caso a primeira instrução define p como um ponteiro para B, embora não o inicialize. A segunda instrução, no entanto, continua errada: o primeiro operando do operador de selecção de membro -> deveria ser um ponteiro. Neste caso nem sequer é um valor: é um tipo, o que é um erro.F B& b; b.bx = 1;
Aqui é a primeira instrução que está errada. Qualquer referência tem de ser inicializada. Logo, a referência b deveria ter sido inicializada com uma instância qualquer de B.[2 valores]
F B::B(int aa, int bb) : a(aa), bx(bb) {}
A variável membro a pertence à classe base A, pelo que não pode ser inicializada directamente na classe derivada (o problema não é a ser privada de A).V B::B(int aa, int bb) : A(aa), bx(bb) {}
Aqui a inicialização de a faz-se indirectamente (e correctamente...) invocando o construtor da classe base A na lista de inicializadores do construtor da classe derivada B.F B::B(int aa, int bb) : bx(bb) { A(aa); }
Neste caso não é feita uma invocação explícita do construtor de A na lista de inicializadores do construtor de B. Assim, o compilador tenta invocar implicitamente o construtor por omissão de A. Como este não existe, é um erro. Mesmo que o construtor por omissão existisse, o corpo do construtor (entre {}) seria sempre absurdo (embora sem qualquer erro sintáctico): a instrução A(aa); tem como resultado a criação de uma nova instância temporária da classe A que é imediatamente descartada. É uma instrução tão inútil quanto a instrução 1;.[1.5 valores]
B b(1, 2);o que apareceria no ecrã (só uma opção verdadeira)?
A* pa = &b;
pa->g();
V
Execução de A::g().
F
Execução de A::g(). Execução de B::g().
F
Execução de B::g().
A classe B, derivada de A, sobrepõe a sua própria especialização do procedimento g(). Mas este não é virtual na classe base, pelo que a invocação de g() através de um ponteiro para A não tem comportamento polimórfico, ou seja, a versão invocada depende do tipo do ponteiro e não do tipo do objecto apontado.[1.5 valores]
class Tarefa {
public:// Construtor (não há construtor por omissão):
Tarefa(string descrição, string notas);// Destrutor virtual:
virtual ~Tarefa() {}// Acesso à descrição e às notas:
string descrição() const;
string notas() const;// Devolve duração da tarefa (em dias):
virtual int duração() const = 0;// Devolve custo total da tarefa (em escudos):
virtual int custo() const = 0;// Devolve a mão de obra necessária para a execução da tarefa em cada dia (em operários):
virtual int mãoDeObra() const = 0;private:
string descrição_;
string notas_;
};
Não é necessário nesta alínea definir qualquer um dos métodos da classe TarefaMecanizada.
[4 valores]
class TarefaMecanizada : public Tarefa {
public:
// Construtor:
TarefaMecanizada(string descrição, string notas,
int duração, int custo_diário,
const string& tipo_de_equipamento);// Sobreposição de funções às funções puramente virtuais da classe base:
int duração() const;
int custo() const;
int mãoDeObra() const;// Funções membro de acesso aos dados:
int custoDiário() const;
string tipoDeEquipamento() const;private:
// Variáveis membro:
int duração_;
int custo_diário_;
string tipo_de_equipamento_;
};
[3 valores]
TarefaMecanizada::TarefaMecanizada(string descrição, string notas,
int duração, int custo_diário,
const string& tipo_de_equipamento)
: Tarefa(descrição, notas), duração_(duração),
custo_diário_(custo_diário),
tipo_de_equipamento_(tipo_de_equipamento) {
}
[3 valores]
int TarefaMecanizada::mãoDeObra() const {
return 0;
}
template <typename I>Admita que a definição acima se encontra num ficheiro pilha.h.
class Pilha {
public:
// Construtor da classe:
Pilha();
// Coloca cópia de item no topo da pilha:
void põe(const I& item);
// Retira o item que está no topo da pilha:
void tira();
// Devolve cópia do item que está no topo da pilha:
I topo() const;
// Devolve true sse a pilha estiver vazia:
bool vazia() const;
// Devolve o número de itens na pilha:
int altura() const;
private:
...
};
Escreva um pequeno programa, utilizando o modelo de pilha fornecido, que peça ao utilizador para introduzir um texto e verifique se os seus parênteses estão correctamente balanceados. Preveja apenas parêntesis de dois tipos: curvos (()) e rectos ([]). Admita que o texto termina no primeiro ponto final (.) encontrado. O programa deve ignorar o texto em si: só os parânteses são importantes. Por exemplo, dadas as seguintes expressões, o programa deve escrever os erros indicados a itálico:
) Aqui ( há ) ( [ gato ] ) .Utilize o seguinte esqueleto de programa (não precisa de o reproduzir, basta identificar os três troços de programa a escrever: A, B e C):
Fecho sem correspondente abertura.
( Aqui ] também .
Fecho com tipo de parênteses errado.
( Oops .
Parênteses por fechar.
[ este ) ( ] ] ) é ( ) [ ( comprido ) ( ) ] ( (.
Fecho com tipo de parênteses errado.
Fecho com tipo de parênteses errado.
Fecho sem correspondente abertura.
Fecho sem correspondente abertura.
Parênteses por fechar.
Parênteses por fechar.
#include <iostream>[3 valores]#include "pilha.h"
int main() {
std::cout << "Introduza um texto terminado por um ponto final."
<< std::endl;... // A preencher. Troço A.
char c;
while(std::cin.get(c) && c != '.')
if(c == '(' || c == ')' || c == '[' || c == ']') {
... // A preencher. Troço B.
}... // A preencher. Troço C.
}
#include <iostream>#include "pilha.h"
int main() {
std::cout << "Introduza um texto terminado por um ponto final."
<< std::endl;Pilha<char> abertos;
char c;
while(std::cin.get(c) && c != '.')
if(c == '(' || c == ')' || c == '[' || c == ']') {
if(c == '(' || c == '[')
abertos.poe(c);
else if(abertos.vazia())
std::cout << "Fecho sem correspondente abertura."
<< std::endl;
else {
if((c == ']' && abertos.topo() != '[') ||
(c == ')' && abertos.topo() != '('))
std::cout << "Fecho com tipo de parênteses errado."
<< std::endl;
abertos.tira();
}
}while(!abertos.vazia()) {
std::cout << "Parênteses por fechar" << std::endl;
abertos.tira();
}
}
[2 valores]
Um método deve ser puramente virtual quando corresponder a uma operação que faz sentido para qualquer classe concreta derivada da classe em causa, mas que não seja possível definir para esta classe (classe base). Isso acontece quando a classe em causa representa um conceito abstracto, como uma forma ou um género na taxonomia dos seres vivos. quando um método é declarado como puramente virtual, a respectiva classe não é obrigada a defini-lo, embora o possa fazer. Uma classe com algum método puramente virtual, ou que tenha herdado algum método puramente virtual ao qual não tenha sobreposto um método não puramente virtual, diz-se, do ponto de vista do C++, abstracta. As classes abstractas em C++ não podem ser instanciadas, ou seja, não podem existir instâncias (objectos ou variáveis, dinâmicas ou não) desse tipo.