Identificação
Nome do aluno: _______________________________________________

Número do aluno:  ______________________

Turma: ____________

Exame de 1 ª época

Programação Orientada para Objectos

IGE e ETI

2º semestre de 2000/2001

ISCTE


Notas:

Recibo do exame de 1ª época de Programação Orientada para Objectos (IGE e ETI), 2º semestre de 2000/2001, ISCTE:
Nome do aluno: _______________________________________________
Número do aluno:  ______________________
Assinatura do docente: ________________________________________

Questão 1

Assinale com V (Verdadeiro) as expressões que estão correctas e com F (Falso) as que estão incorrectas.

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.

Considere que todas as operações têm métodos associados mesmo quando não são explicitamente indicados.

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 Base {

  public:
    Base(int a);
    virtual ~Base() {}

    string nomeDoTipo() const;

    virtual void método();

    int a_;

};

string Base::nomeDoTipo() const 
{
    return "Base"; 
}


void Base::método() 
{
    cout << nomeDoTipo() << endl;
}


class Derivada: public Base {

  public:
    Derivada(int d);

    string nomeDoTipo() const;

    virtual void método();

  private:

    int d_;
};

string Derivada::nomeDoTipo() const 
{
    return "Derivada";
}


void Derivada::método()
{
    cout << nomeDoTipo() << endl;
}


int main() 
{

    ...
} 

1.1  Quais das seguintes instruções estão correctas?

__ Base base;
__ Derivada.a_ = 2;
__ Derivada.d_ = 2;

[cotação: 1,5]

1.2  Considere a seguinte a função main():

int main()
{

    Base* base = new Derivada(3);
    Derivada* derivada = new Derivada(4);

    base->método();
    derivada->método();

    delete base;
    delete derivada;
}

qual o resultado produzido pelo programa (apenas uma das respostas está correcta)?

__  Base
    Derivada

__  Base
    Base

__  Derivada
    Derivada

[cotação: 0,5]

1.3  Quais das seguintes afirmações estão correctas?

__ Os objectos da  classe Base herdam o comportamento definido na classe Derivada.
__ A classe Base é abstracta porque tem operações abstractas.

[cotação: 1]

1.4  Dado o seguinte código:

double a = 3.0;
int* b = 0;
float c = 3.14;
int const* d = new int[4];
int* const e = new int[5];

Quais das seguintes instruções estão correctas?

__  int* resultado = a < 3.5 ? a : 3.5;
__  b = &c;
__  ++d;
__  ++e;

[cotação: 2]

Questão 2

Uma fábrica de carpintaria dedica-se à construção de ilhargas, prateleiras e outros componentes procurados por quem pretende construir estantes.

A determinada altura, a administração da fábrica decidiu vender os seus produtos directamente ao público.

O responsável pela venda directa achou necessária uma aplicação que apoiasse o seu sector de vendas e que fosse responsável pela manutenção do catálogo de componentes a vender: Gestor de Catálogo.  Assim,  através desta aplicação é possível consultar informação sobre os componentes, podendo encontrar-se resposta sobre dimensões e preços unitários.

Cada componente a vender caracteriza-se por:

Existem vários tipos de componentes dos quais se destacam as ilhargas e as prateleiras.  É objectivo da fábrica aumentar a diversidade de tipos de componentes que fabrica e vende directamente ao público.

As ilhargas são as partes laterais das estantes e podem suportar  prateleiras e outros componentes.  Existe uma distância mínima, descrita em centímetros, entre os componentes suportados pelas ilhargas.  Esta medida serve apenas para informar o cliente, não limita a venda de prateleiras.  A fabrica produz ilhargas de dimensões variadas.

As prateleiras podem apoiar-se em ilhargas ou directamente na parede (todas as prateleiras são polivalentes) mas, em ambos os casos, é necessário saber o número de suportes precisos para o apoio.

Para a concepção da aplicação de gestão do catálogo foi pensado usar as classes Componente, Ilharga e Prateleira.  A classe Componente representa o que é comum a todas os componentes, a classe Ilharga e a classe Prateleira representam os componentes ilharga e prateleira, respectivamente.

Existem outros componentes como gavetas, armários, etc., mas para a resolução deste grupo de perguntas considere apenas os componentes prateleiras e ilhargas. Contudo, decida sempre por soluções que permitam facilmente estender o Gestor de Catálogo a outros componentes.

A definição de qualquer das classes deve possuir operações para:

2.1  Defina as classes Componente, Ilharga e Prateleira.  Não defina nenhum método.

[cotação: 1,5]

2.2  Defina os  métodos responsáveis pela escrita no canal  do conteúdo de um objecto pertencente à classe Ilharga.  O método deve escrever todos os dados que definem uma ilharga.  O formato é deixado ao seu critério.

[cotação: 1,5]

Após alguns meses de experiência do sistema de venda directa achou-se conveniente vender composições de componentes, ou seja, módulos, destinados a estantes.  Os módulos para estantes possuem um número indeterminado de ilhargas, prateleiras e outros componentes.

A resolução desta questão é uma simplificação da resolução realmente necessária.  Considere que é possível conceber um módulo de estantes em que alguns dos componentes são outros módulos de estantes, o que não é verdade.

O cálculo do preço de cada módulo é a soma dos preços afectados dos possíveis descontos.

A altura do módulo é a altura máxima das ilhargas (que podem ser de alturas diferentes).  O largura do módulo é a soma da largura das ilhargas e da largura máxima dos componentes.  A profundidade é o máximo das profundidades entre as ilhargas e os componentes.

2.3 Defina a classe MóduloDeEstante, que representa o módulo de estante descrito anteriormente, mas não defina os seus métodos.  Guarde a lista de componentes que compõe um módulo usando a classe list<Componente const*>.

[cotação: 1,5]

2.4  Defina o método responsável pelo cálculo do preço de um módulo.

[cotação: 1]

A aplicação Gestor de Catálogo  baseia-se na classe GestorDeCatálogo que disponibiliza as seguintes funcionalidades:

2.5  Defina a classe GestorDeCatálogo.  Não especifique o conteúdo de qualquer dos métodos.  Use a classe list<Componente*> para definir a lista de componentes mantida pela classe GestorDeCatálogo.

[cotação: 0,5]

2.6  Defina o método responsável pela adição de um componente à lista de componentes mantida pela classe GestorDeCatálogo.  Não se esqueça que este método deve manter a lista ordenada segundo o referido no enunciado, por ordem crescente ou decrescente (fica ao seu critério).

[cotação: 1,5]

2.7  Defina o destrutor de GestorDeCatálogo.

[cotação: 0,5]

2.8  Defina o método da classe GestorDeCatálogo responsável pela inserção das definições de componentes num canal.

Os dados inseridos no canal devem obedecer ao seguinte formato:

número_de_componentes
tipo_do_primeiro_componente
...informação do primeiro componente
...
tipo_do_último_componente
...informação do último componente

[cotação: 1]

2.9  Defina o método da classe GestorDeCatálogo responsável pela extracção das definições dos componentes a partir de um canal.

Os dados extraídos do canal devem obedecer ao mesmo formato usado na alínea anterior.

[cotação: 1]

Questão 3

Considere a seguinte estrutura

struct Elo {
    typedef int Item;

    Item item;     //
o item propriamente dito.
    Elo* anterior; // ponteiro para o elo anterior na cadeia.
    Elo* seguinte; // ponteiro para o elo seguinte na cadeia.

    ///
Construtor de elos.  Simplifica a construção de novos elos.
    Elo(Elo* anterior = 0, Elo* seguinte = 0, Item const& item = Item());
};

usada para representar os elos de uma cadeia duplamente ligada (não-circular) com guardas.

3.1  Defina o procedimento void põe(Elo* elo_anterior, Elo* elo_seguinte, Elo::Item const& novo_item) que inserira um novo item numa cadeia duplamente ligada (não-circular) com guardas.  O elo do novo item é inserido entre os dois elos apontados por elo_anterior e elo_seguinte, que se supõem sucessivos.  Estes ponteiros não podem nunca ser nulos.

Faça desenhos da cadeia em várias situações!  Só assim resolverá com sucesso esta alínea.

[cotação: 0,5]

3.2  Implemente uma função que indica se, numa cadeia duplamente ligada (não-circular) com guardas, um dado item existe (ou não).  A cadeia é representada por um ponteiro para o elo antes do primeiro (guarda inicial) e por um ponteiro para o elo depois do último (guarda final).

Faça desenhos da cadeia em várias situações!  Só assim resolverá com sucesso esta alínea.

[cotação: 1]

Questão 4

Considere a seguinte definição (incompleta) de uma pilha de inteiros:

class PilhaInt {
  public:
    typedef int Item;

    ///
Construtor da classe.  O argumento é a capacidade da pilha.
    PilhaInt(int capacidade = capacidade_inicial);

    PilhaInt(PilhaInt const& p);
    ~PilhaInt();

        void põe(Item const& novo_ item);

        ...

    bool estáVazia() const;

    bool estáCheia() const;

    /// Devolve o número de itens na pilha, i.e., a sua altura.
    int altura() const;

   
...

  private:
    void duplicaCapacidade() const;

    static int const capacidade_inicial = 32;

    mutable int capacidade;

    int altura_;
    // Matriz dinâmica dos itens:
    mutable Item* itens;
};

inline void PilhaInt::põe(Item const& novo_ item) {

    if(altura == capacidade)
        //
Não há espaço, logo duplica-se a capacidade:

        duplicaCapacidade();

    //
Agora já há espaço, pode-se inserir normalmente:

    itens[altura++] = novo_item;
}

void PilhaInt::duplicaCapacidade()

{
    Item* novos_itens = new Item[2 * capacidade];

    //
Copia-se para a nova matriz os itens que estavam na matriz original:

    for(int i = 0; i != altura; ++i)
        novos_itens[i] = itens[i];
 
    capacidade *= 2;


    //
Destrói-se a matriz dos itens original:
    delete[] itens;


    // A matriz dos itens passa a ser a nova matriz construída (já com os itens antigos):

    itens = novos_itens;
}

int main()
{

    ...
}

Explique as alterações na definição da classe PilhaInt necessárias para que as falhas por falta de memória redundem no lançamento de uma excepção da classe PilhaInt::MemóriaEsgotada e não através do lançamento da excepção bad_alloc (i.e., traduza o tipo de excepção lançado).  Exemplifique as alterações usando os métodos void PilhaInt::põe(Item const& item) e void PilhaInt::duplicaCapacidade().

Defina as classes e métodos que achar necessários.

[cotação: 2]

Questão 5

Um problema comum em grandes projectos é a existência de entidades (classes, variáveis, funções ou procedimentos) com o mesmo nome, embora desenvolvidos por pessoas, equipas ou empresas diferentes.  Quando isto acontece diz-se que ocorreu uma colisão de nomes.

Indique a solução a adoptar para minimizar a probabilidade de ocorrência deste problema.  Exemplifique usando a definição da classe PilhaInt fornecida na Questão 4 e admitindo que essa classe faz parte de um pacote de contentores de informação fornecido pela empresa O Barato Sai Caro.

[cotação: 1,5]