ISCTE - ETI

Introdução à Programação

Resolução do Exame de 1ª época

1998/1999,  1º semestre, Lisboa, 20 de Fevereiro de 1999



A azul e indentadas uma vez as resoluções dos exercícios.

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.

Qualquer alínea pode ter zero ou mais respostas correctas.  Cada resposta correctamente assinalada vale 0,5 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, int b = 1, int c = 0);
    int f2(int);
    bool f3(int);
};

int main()
{
    int i = 1, j = 2, k = 3, l = 2;
    ...
}

1.a)  Quais das seguintes instruções estão correctas?

 F  A m[100];

A classe A tem um construtor que não pode ser ser invocado sem argumentos (o primeiro parâmetro não tem valor por omissão) e não tem construtor por omissão.  Assim, o compilador não sabe como construir os elementos da matriz.
 F  A a;
A classe A não tem construtor por omissão (ver resposta anterior).
 V  A a(1);
A classe A tem um construtor com um único parâmetro int.
[cotação: 1,5]

1.b)  Assuma que está correctamente declarada uma variável a da classe A e nada mais.  Quais das seguintes instruções estão correctas?

 F  int b = a.x[i];

A variável (matriz) membro x é privada.
 V  int b = a.z;
A variável membro z é públic e int.
 F  int b = a.f1();
A função membro f1() é privada.
 F  int b = f2(a);
A função f2() é membro de A, pelo que tem de ser invocada através de uma instância de A.  Além disso tem um parâmetro int, e não A.
[cotação: 2]

1.c)  Assuma que as seguintes instruções são dadas dentro de uma função membro da classe A, que não tem quaisquer parâmetros, e que não são declaradas quaisquer variáveis dentro dessa função.  Quais das seguintes instruções estão correctas?

 F  int b = f1() + x;

Não se pode somar uma matriz a um int.
 F  int b = f2();
A função f2() tem uma parâmetro int sem valor por omissão, pelo tem de ser invocada com um argumento.
 V  int b = z;

[cotação: 1,5]

2.a)  Considere uma função int menor(const int m[], const int n) que deve devolver o valor do menor elemento de uma matriz m que contém n elementos.  Dada a pré-condição n > 0, indique a condição objectivo (CO), a condição invariante (CI) e a guarda (G) do ciclo necessário à construção desta função.

[cotação: 1]

CO: (E j : 0 <= j < n : menor = m[j]) e (Q j : 0 <= j < n : menor <= m[j])
CI: (E j : 0 <= j < i : menor = m[j]) e (Q j : 0 <= j < i : menor <= m[j]) e 0 <= i <= n
G: i <> n
2.b)  Defina completamente a função indicada na alínea anterior.

[cotação: 1]

int menor(const int m[], const int n)
{
    int menor = m[0];
    for(int i = 1; i != n; ++i)
        if(m[i] < menor)
            menor = m[i];
    return menor;
}
3.  Considere o problema da criação de um pequeno programa para gerir as pistas de aterragem de um aeroporto.  Para cada avião é necessário guardar a pista em que vai aterrar (entre 0 e 9) e a identificação do avião (um número inteiro positivo inferior a 231).  Durante o funcionamento do programa, o seu operador irá necessitar de inserir numa lista os aviões que pediram licença para aterrar, retirar dessa lista os aviões que já tenham aterrado e verificar se um determinado avião ainda se encontra na lista à espera para aterrar.

3.a)  Defina uma classe Avião que possa guardar a informação sobre a identificação de um avião e a pista onde está previsto ele aterrar.  Deve existir um construtor para a classe avião com dois parâmetros (identificação e pista de aterragem) e um construtor sem parâmetros (construtor por omissão).  Deve declarar também a sobrecarga do operador de igualdade (==) que compara dois aviões e devolve true caso as suas identificações sejam iguais e false no caso contrário.  Deve também declarar a sobrecarga do operador de inserção num canal (<<) que permite visualizar no ecrã os dados de um avião. Não é necessário nesta questão definir qualquer um dos métodos (funções ou procedimentos membro) da classe.

[cotação: 2]

3.b)  Defina uma classe ListaDeAterragem que deve guardar a informação sobre um conjunto de aviões (no máximo 100).  Deve declarar os métodos necessários para inserir um avião na lista e retirar um avião da lista dada a sua identificação.  Deve também declarar uma função que, dada a identificação de um avião, devolve true se este já se encontra na lista e false caso contrário.  Não é necessário nesta questão definir qualquer um dos métodos da classe.

[cotação: 2]

3.c)  Defina os construtores da classe Avião.

[cotação: 1]

3.d)  Defina o operador de igualdade (==) entre dois aviões sabendo que dois aviões são iguais caso a sua identificação seja igual independentemente da pista onde devem aterrar.

[cotação: 1]

3.e)  Defina o operador de inserção num canal (<<) para a classe Avião que permita dar as seguintes instruções:

Avião a(1, 1);
cout << a << endl;
e cujo resultado é aparecer no ecrã o seguinte texto:
Avião 1: vai aterrar na pista 1.
[cotação: 1]

3.f)  Defina o método da classe ListaDeAterragem que retira um avião (dada a sua identificação) da lista de aviões.

[cotação: 2]

3.g)  Faça um pequeno programa que use uma variável da classe ListaDeAterragem e insira nesta lista três aviões cujos dados devem ser introduzidos pelo utilizador.  Não é necessário repetir a declaração ou definição das classes ListaDeAterragem e Avião.

[cotação: 1]

Apresenta-se a solução completa:

#include <iostream>
#include <cstdlib>

using namespace std;

// 3.a)

class Avião {
public:
    Avião();
    Avião(int, int);
    int id() const;
    int pista() const;

private:
    int id_;
    int pista_;
};

bool operator == (const Avião&, const Avião&);
ostream& operator << (ostream&, const Avião&);

// 3.c)

inline Avião::Avião()
    : id_(-1), pista_(0) {
}

inline Avião::Avião(int id, int pista)
    : id_(id), pista_(pista) {
    if(pista < 0 || pista > 9) {
        cerr << "Pista inválida!" << endl;
        exit (1);
    }
}

// Extras:

inline int Avião::id() const {
    return id_;
}

inline int Avião::pista() const {
    return pista_;
}

// 3.d)

inline bool operator == (const Avião& a, const Avião& b) {
    return a.id() == b.id();
}

// 3.e)

inline ostream& operator << (ostream& saida, const Avião& avião) {
    return saida << "Avião " << avião.id() << ": vai aterrar na pista "
                 << avião.pista() << '.';
}

// 3.b)

class ListaDeAterragem {
public:
    static const int número_máximo_de_aviões = 100;

    ListaDeAterragem();
    void insere(const Avião&);
    void remove(int id);
    bool contém(int id) const;

private:
    Avião lista_de_aviões[número_máximo_de_aviões];
    int número_de_aviões; // correntemente na lista.
};

// Extras:

ListaDeAterragem::ListaDeAterragem()
    : número_de_aviões(0) {
}

bool ListaDeAterragem::contém(int id) const
{
    for(int i = 0; i != número_de_aviões; ++i)
        if(lista_de_aviões[i].id() == id)
            return true;
    return false;
}

void ListaDeAterragem::insere(const Avião& avião)
{
    if(contém(avião.id())) {
        cerr << "Avião já está na lista!" << endl;
        exit(1);
    }
    if(número_de_aviões == número_máximo_de_aviões) {
        cerr << "Avião não cabe na lista!" << endl;
        exit(1);
    }
    lista_de_aviões[número_de_aviões++] = avião;
}

// 3.f)

void ListaDeAterragem::remove(int id)
{
    int i = 0;
    while(i != número_de_aviões && lista_de_aviões[i].id() != id)
        ++i;
    if(i != número_de_aviões)
        lista_de_aviões[i] = lista_de_aviões[--número_de_aviões];
    else {
        cerr << "Esse avião não existe na lista de aterragem!" << endl;
        exit(1);
    }
}

// 3.g)

int main()
{
    ListaDeAterragem lista;
    for(int i = 0; i != 3; ++i) {
        cout << "Introduza identificação e pista do avião: ";
        int id, pista;
        cin >> id >> pista;
        lista.insere(Avião(id, pista));
    }
}

4.  Defina completamente uma classe Data que permita manipular datas do ano.  Deve definir o construtor (que verifica a correcção dos valores dos argumentos).  Deve definir o operador ++ prefixo, que permite adicionar um dia a uma data, e o operador de inserção num canal <<,  que permita visualizar uma data no ecrã no formato ano/mês/dia.  A implementação deve prever anos bissextos.  São anos bissextos todos os anos que sejam múltiplos de 4, excepto se forem múltiplos de 100 e não forem múltiplos de 400.  Nesta questão devem ser definidos todos os métodos desta classe.

Esta classe deverá poder ser usada do seguinte modo:

Data a(1999, 2, 28);
++a;
cout << a << endl;
que deve fazer aparecer no ecrã o seguinte texto:
1999/3/1
[cotação: 2]
class Data {
  public:
    Data(int ano, int mês, int dia);
    int ano() const;
    int mês() const;
    int dia() const;
    bool éBissexto() const;
    int diasDoMês() const;
    Data& operator ++ ();

  private:
    int ano_;
    int mês_;
    int dia_;
};

inline Data::Data(int ano, int mês, int dia)
    : ano_(ano), mês_(mês), dia_(dia) {
    if(mês <= 0 || mês > 12) {
        cerr << "Mês inválido!" << endl;
        exit (1);
    }
    if(dia <= 0 || dia > diasDoMês()) {
        cerr << "Dia inválido!" << endl;
        exit (1);
    }
}

inline int Data::ano() const {
    return ano_;
}

inline int Data::mês() const {
    return mês_;
}

inline int Data::dia() const {
    return dia_;
}

inline bool Data::éBissexto() const {
    return ano_ % 4 == 0 && (ano_ % 100 != 0 || ano_ % 400 == 0);
}

inline int Data::diasDoMes() const {
    if(mês_ == 2)
        if(éBissexto())
            return 29;
        else
            return 28;
    else if(mês_ == 4 || mês_ == 6 || mês_ == 9 || mês_ == 11)
        return 30;
    else
        return 31;
}

inline Data& Data::operator ++ () {
    if(++dia_ > diasDoMes()) {
        dia_ = 1;
        if(++mes_ > 12) {
            mes_ = 1;
            ++ano_;
        }
    }
    return *this;
}

inline ostream& operator << (ostream& saída, const Data& data) {
    return saída << data.ano() << '/' << data.mes() << '/'
                 << data.dia();
}

5. Escreva um pequeno texto (sucinto) que explique as vantagens da metodologia de Dijkstra no desenvolvimento de ciclos.

[cotação: 1]

Ver folhas teóricas, Capítulo 4, Secção 4.3.