ISCTE - ETI

Introdução à Programação

Resolução da 2ª Frequência

1998/1999,  1º semestre



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

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.

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;
    int y;

  public:
    int z;
    A(int a = 0);
    int f1();
    int f2(int);
    bool f3(int);
};

int main() {
    int i = 1, j = 2;
   
...
}
a)  Quais das seguintes instruções estão correctas?

 V  A a = A();

Forma rebuscada de dizer: A a;.  É invocado o construtor por cópia para construir a a partir de uma instância temporária de A construída invocando o construtor de A que pode não receber qualquer argumento (o construtor de A tem um parâmetro que tem valor por omissão).
 V  A a;
Ver primeira resposta.
 V  A a(1);
Invocado o construtor de A que aceita um inteiro como argumento.
 F  A a(i, j);
Não há nenhum construtor de A que aceite dois argumentos.
 [cotação: 2]

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

 V  a.z = 1;

A variável membro z é pública.
 F  if(A::f3()) cout << "bla bla";
A função membro f3() tem de ser invocada através de uma instância.  Além disso exige um argumento inteiro.
 V  int z = a.f2(3);

 [cotação: 1.5]

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

 F  a.x = 1;

Não existe qualquer variável a...
 V  A::x = 1;
Qualificar a variável x com o nome da classe é redundante, mas válido.  Ver instrução seguinte.
 V  x = 1;

 [cotação: 1.5]

Questão 2

2.a)  Declare uma classe Colecção que possa guardar um máximo de 100 valores do tipo inteiro (todos de valor diferente) e o número de ocorrências de cada um desses valores.  Quando é inserido um valor que já se encontra na colecção apenas deve ser incrementado o número de ocorrências desse valor.  Quando é inserido um valor que não existe, este passa a fazer parte da colecção e é indicado que existe apenas uma ocorrência deste valor na colecção.  Não é necessário nesta alínea definir qualquer um dos métodos.

Devem existir métodos para:

Deve também ser declarada a sobrecarga do operador += que adiciona todos os elementos da colecção dada como segundo operando à colecção dada como primeiro operando (i.e., aquela que executa a operação).  Deve devolver uma referência para a colecção alterada.

A classe deve também ter um construtor que possa ser invocado sem quaisquer argumentos.

A declaração da classe deve ser tal que o seguinte exemplo de utilização seja válido:

Colecção A, B;

A.insere(1);
A.insere(2);
// A = (1 [uma vez], 2 [uma vez])
B.insere(1);
B.insere(3);
// B = (1 [uma vez], 3 [uma vez])
B += A;
// B = (1 [duas vezes], 2 [uma vez], 3 [uma vez])
cout << B.ocorrências(1) << endl; // escreve 2.

 [cotação: 3]

2.b) Defina o construtor da classe Colecção.  Este construtor deve inicializar coerentemente as variáveis de instância da classe.

[cotação: 3]

2.c)  Defina totalmente o método da classe Colecção que, dado um argumento inteiro, devolve o número de ocorrências do seu valor na colecção (caso esse valor não exista este método deve devolver zero).

[cotação: 3]

 2.d)  Assuma que todos os métodos declarados na classe colecção estão bem definidos, excepto o operador +=. Defina o operador += para a classe Colecção.  Esta operação adiciona todos os elementos da colecção dada como segundo operando à colecção dada como primeiro operando (i.e., aquela através da qual se executa a operação).  Deve devolver uma referência para a colecção alterada (para isso use a instrução return *this;).

Não esqueça que a operação insere() apenas insere uma ocorrência de um elemento.  Caso um elemento ocorra n vezes numa colecção e m vezes noutra, o número total de ocorrências desse elemento na colecção devolvida deve ser n + m.

Assuma como pré-condição que há espaço para todos os valores.

[cotação: 3]

Apresenta-se a solução completa incluindo um programa de teste:
#include <iostream>
#include <cstdlib>

using namespace std;

class Colecção {
  public:
    typedef int Elemento;
    static const int número_máximo_de_elementos = 100;

    Colecção();
    void insere(const Elemento&);
    void remove(const Elemento&);
    bool vazia() const;
    int ocorrências(const Elemento&) const;
    bool contém(const Elemento&) const;
    Colecção& operator += (const Colecção&);
    friend ostream& operator << (ostream&, const Colecção&);

  private:
    Elemento elementos[número_máximo_de_elementos];
    int repetições[número_máximo_de_elementos];
    int número_de_elementos;
    int procura(const Elemento&) const;
};

inline Colecção::Colecção() : número_de_elementos(0) {
}

int Colecção::procura(const Elemento& elemento) const {
    int i = 0;
    while(i != número_de_elementos && elementos[i] != elemento)
        ++i;
    return i;
}

void Colecção::insere(const Elemento& elemento) {
    int i = procura(elemento);
    if(i == número_de_elementos)
        if(número_de_elementos == número_máximo_de_elementos) {
            cerr << "Colecção cheia!" << endl;
            exit(1);
        } else {
            elementos[número_de_elementos] = elemento;
            repetições[número_de_elementos++] = 1;
        }
    else
        ++repetições[i];
}

void Colecção::remove(const Elemento& elemento) {
    int i = procura(elemento);
    if(i == número_de_elementos) {
        cerr << "Elemento inexistente!" << endl;
        exit(1);
    }
    if(--repetições[i] == 0) {
        elementos[i] = elementos[--número_de_elementos];
        repetições[i] = repetições[número_de_elementos];
    }
}

inline bool Colecção::vazia() const {
    return número_de_elementos == 0;
}

int Colecção::ocorrências(const Elemento& elemento) const {
    int i = procura(elemento);
    return i == número_de_elementos ? 0 : repetições[i];
}

bool Colecção::contém(const Elemento& elemento) const {
    return procura(elemento) != número_de_elementos;
}

Colecção& Colecção::operator += (const Colecção& c) {
    for(int i = 0; i != c.número_de_elementos; ++i) {
        int j = procura(c.elementos[i]);
        if(j == número_de_elementos)
            if(número_de_elementos == número_máximo_de_elementos) {
                cerr << "Colecção cheia!" << endl;
                exit(1);
            } else {
                elementos[número_de_elementos] = c.elementos[i];
                repetições[número_de_elementos++] = c.repetições[i];
            }
        else
            repetições[j] += c.repetições[i];
    }
    return *this;
}

ostream& operator << (ostream& saída, const Colecção& c) {
    saída << '(';
    for(int i = 0; i != c.número_de_elementos; ++i) {
        saída << c.elementos[i] << ':' << c.repetições[i];
        if(i != c.número_de_elementos - 1)
            saída << ", ";
    }
    return saída << ')';
}

int main()
{
    Colecção a;
    a.insere(1);
    a.insere(2);
    a.insere(2);
    cout << a << endl;
    Colecção b;
    b.insere(0);
    b.insere(2);
    b.insere(3);
    b.insere(4);
    b.remove(4);
    cout << b << endl;
    b += a;
    cout << b << endl;
}

Depois da execução deste programa aparece

(1:1, 2:2)
(0:1, 2:1, 3:1)
(0:1, 2:3, 3:1, 1:1)
no ecrã.
Questão 3
Diga para que servem as funções amigas (friend) duma classe.

[cotação: 3]
 

As funções amigas duma classe são funções que, não sendo membro da classe, têm no entanto acesso aos seus membros privados.  Sempre que possível (sem compremeter a funcionalidade nem a eficiência) devem ser evitadas, pois violam o princípio do encapsulamento, pervertendo adicionalmente a estruturação do programa.