ISCTE - IGE

Programação I

Resolução do Exame de 1ª época

1998/1999,  1º semestre, 22 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 = 0, 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?

 V  A m[1];

É possível definir uma matriz com elementos da classe A porque o construtor definido pode ser invocado sem argumentos, i.e., funciona como um construtor por omissão.
 F  A a(i, j, k, l);
O construtor da classe A só tem três parâmetros.
 V  A a(1, i, 2);
O construtor da classe A tem três parâmetros, todos do tipo int, e i é int.
[cotação: 1,5]

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

 F  int b = a.z[3];

A variável membro z não é uma matriz e a é uma matriz e não uma instância da classe A.  A versão correcta seria: a[3].z.
 V  int b = a[2].z;
Ver resposta anterior.
 V  int b = a[0].f2(2);
A função f2() é pública, tem um parâmetro int e devolve um int.
 F  int b = f1();
A função f1() é membro da classe!  Tem de ser invocada através de uma instância da classe (a não ser que seja invocada dentro de outra função ou procedimento membro da classe, o que não é o caso).  Corrigir para:
int b = a[0].f1();
não resolve o problema, pois f1() é privada!
[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 = z + x;

Não se pode somar um inteiro com uma matriz!
 F  int b = f2();
A função f2() tem um parâmetro (que não tem valor por omissão)!
 V  int b = f1() + y;

[cotação: 1,5]

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

[cotação: 1]

CO: produto = (P j : 0 <= j < n : m[j])
CI: produto = (P j : 0 <= j < i : 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 produto(const int m[], const int n)
{
    int produto = 1;
    for(int i = 0; i != n; ++i)
        produto *= m[i];
    return produto;
}
3.  Considere o problema da criação de um pequeno programa para gerir as contas de um banco.  Para cada conta é necessário guardar o seu saldo (um número decimal) e a sua identificação (um número inteiro positivo inferior a 231).  Durante o funcionamento do programa, o seu operador irá necessitar de inserir novas contas numa lista de contas, retirar da lista contas que foram fechadas e verificar o saldo de uma determinada conta que se encontra na lista.

3.a)  Defina uma classe Conta que possa guardar a informação sobre a identificação e o saldo de uma conta.  Deve existir um construtor para a classe  Conta com dois parâmetros (identificação e saldo inicial), e se possa invocar indicando a identificação e o saldo inicial, apenas a identificação ou sem qualquer argumento.  Deve declarar também a sobrecarga do operador de igualdade (==) que compara duas contas 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 uma conta.  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 ListaDeContas que deve guardar a informação sobre uma lista de contas (no máximo 100).  Deve declarar os métodos necessários para inserir um conta na lista e retirar uma conta da lista dada a sua identificação.  Deve também declarar uma função que, dada a identificação de uma conta existente na lista, devolve o saldo dessa conta.  Não é necessário nesta questão definir qualquer um dos métodos da classe.

[cotação: 2]

 3.c)  Defina o construtor da classe Conta.

[cotação: 1]

3.d)  Defina o operador de igualdade (==) entre duas contas sabendo que duas contas são iguais caso a sua identificação seja igual independentemente do seu saldo.

[cotação: 1]

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

Conta a(1, 200.5);
cout << a << endl;
e cujo resultado é aparecer no ecrã o seguinte texto:
A conta 1 tem 200.5 mil escudos.
[cotação: 1]

3.f)  Defina o método da classe ListaDeContas que retira uma conta (cuja identificação é dada como argumento) da lista de contas.

[cotação: 2]
3.g)  Faça um pequeno programa que use uma variável da classe ListaDeContas e peça para inserir nesta lista três contas cujos dados devem ser introduzidos pelo utilizador.  Não é necessário repetir a declaração ou definição da classe ListaDeContas.

[cotação: 1]

Apresenta-se a solução completa:

#include <iostream>
#include <cstdlib>

using namespace std;

// 3.a)

class Conta {
  public:
    Conta(int id = -1, double saldo = 0.0);
    int id() const;
    double saldo() const;

  private:
    int id_;
    double saldo_;
};

bool operator == (const Conta&, const Conta&);
ostream& operator << (ostream&, const Conta&);

// 3.c)

inline Conta::Conta(int id, double saldo)
    : id_(id), saldo_(saldo) {
}

// Extras:

inline int Conta::id() const {
    return id_;
}

inline double Conta::saldo() const {
    return saldo_;
}

// 3.d)

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

// 3.e)

inline ostream& operator << (ostream& saída, const Conta& conta) {
    return saída << "A conta " << conta.id() << ", tem " << conta.saldo()
                 <<" mil escudos.";
}

// 3.b)

class ListaDeContas {
  public:
    static const int número_máximo_de_contas = 100;

    ListaDeContas();
    void insere(const Conta&);
    void remove(int);
    bool contém(int) const;
    double saldoDe(int) const;

  private:
    Conta lista[número_máximo_de_contas];
    int número_de_contas; // correntemente na lista.
};

// Extras:

inline ListaDeContas::ListaDeContas()
    : número_de_contas(0) {
}

bool ListaDeContas::contém(int id) const
{
    for(int i = 0; i != número_de_contas; ++i)
        if(lista[i].id() == id)
            return true;
    return false;
}

double ListaDeContas::saldoDe(int id) const
{
    for(int i = 0; i != número_de_contas; ++i)
        if(lista[i].id() == id)
            return lista[i].saldo();
    cerr << "Conta inexistente!" << endl;
    exit(1);
}

void ListaDeContas::insere(const Conta& conta)
{
    if(contém(conta.id())) {
        cerr << "Conta já existe!" << endl;
        exit(1);
    }
    if(número_de_contas == número_máximo_de_contas) {
        cerr << "Conta não cabe na lista!" << endl;
        exit(1);
    }
    lista[número_de_contas++] = conta;
}

// 3.f)

void ListaDeContas::remove(int id) {
    for(int i = 0; i != número_de_contas; ++i)
        if(lista[i].id() == id) {
            lista[i] = lista[--número_de_contas];
            return;
        }
    cerr << "Conta inexistente!" << endl;
    exit(1);
}

// 3.g)

int main()
{
    ListaDeContas lista;

    for(int i = 0; i != 3; ++i) {
        cout << "Introduza a identificação e o saldo: ";
        int id;
        double saldo;
        cin >> id >> saldo;
        lista.insere(Conta(id, saldo));
    }
}

4.  Defina completamente uma classe HoraDoDia que permita manipular horas do dia.  Deve declarar a classe e definir o construtor (que verifica a correcção dos argumentos).  Deve definir o operador de subtracção (-) que permite calcular a diferença entre duas variáveis do tipo HoraDoDia.

Deve definir o operador de inserção num canal (<<) que permite visualizar no ecrã uma variável do tipo HoraDoDia no seguinte formato: horas:minutos.  Nesta questão é necessário definir todos os métodos da classe HoraDoDia.  A implementação deve prever a seguinte possível utilização:

HoraDoDia a(23, 15);
HoraDoDia b(1, 0);
cout << b - a << endl;
esta última operação deve fazer aparecer no ecrã o seguinte texto:
1:45
Note que a diferença entre duas horas do dia é sempre uma hora válida (i.e. entre 0 e 24 exclusivé, e os minutos estão entre 0 e 60 exclusivé).

[cotação: 2]

class HoraDoDia {
public:
    HoraDoDia(int = 0, int = 0);
    int horas() const;
    int minutos() const;
private:
    int horas_;
    int minutos_;
};

inline int HoraDoDia::horas() const {
    return horas_;
}

inline int HoraDoDia::minutos() const {
    return minutos_;
}

inline HoraDoDia::HoraDoDia(int horas, int minutos)
    : horas_(horas), minutos_(minutos) {
    if(horas < 0 || horas >= 24 || minutos < 0 || minutos >= 60) {
        cerr << "Hora inválida!" << endl;
        exit(1);
    }
}

inline HoraDoDia operator- (const HoraDoDia& a, const HoraDoDia& b) {
    int horas = a.horas() - b.horas();
    int minutos = a.minutos() - b.minutos();

    if(minutos < 0) {
        minutos += 60;
        --horas;
    }
 
    if(horas < 0)
        horas += 24;

    return HoraDoDia(horas, minutos);
}

inline ostream& operator << (ostream& saída, const HoraDoDia& h) {
    return saída << h.horas() << ':' << h.minutos();
}

5.  Explique sucintamente o que é uma condição invariante de um ciclo e como demonstraria que uma condição é de facto invariante.

[cotação: 1]

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