Resolução do Exame de 1 ª época

Programação Orientada para Objectos

IGE e ETI

2º semestre de 1999/2000

ISCTE


A azul (e identadas uma vez) as respostas às questões.
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.

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 {
  public:
    A(int a, int b = 0)
        : a(a + b) {
    }
    int h() const { return a; }

  protected:
    int a;
};

class B : public A {
  public:
    B(int a) : A(2*a) { ba = new A(a); }
    virtual int h() const { return 2*a; }
    virtual ~B() {
        f();
        delete ba;
    }
    int g();

  private:
    A* ba;
    void f() const {
        cout << ba->h() << endl;
    }
};

int main()
{
    ...
}

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

 V  A a(7, 0);

O construtor da classe A aceita dois argumentos do tipo inteiros.
 F  A* a = new a(3);
A seguir ao operador new deve aparecer o nome de um tipo. a não é nome de nenhum tipo.
 F  B* b = new B();
Ao construtor da classe B deve ser sempre passado um argumento inteiro.
[1,5 valores]
Questão 1.2
Sendo a função main()
B* b = new B(1);
A* a = b;
cout << " " << b->h() << " " << a->h() << " ";
delete b;
qual o resultado produzido pelo programa (apenas uma das respostas está correcta)?  Atenção aos procedimentos virtuais!

 V   4 2 1
 F   4 4 1
 F   4 2 2

Na primeira instrução é construída uma variável dinâmica da classe B e o seu endereço é colocado no ponteiro b, do tipo B*.  Em seguida o mesmo endereço é colocado no ponteiro a, do tipo A*.  A construção da variável dinâmica é feita invocando o construtor da classe B com o argumento 1.  Esse construtor invoca o construtor da classe base A com argumento 2, que inicializa a variável membro A::a com o valor 2.  Depois o construtor de B constrói um variável dinâmica do tipo A e coloca o seu endereço na variável membro B::ba.  A construção dessa variável dinâmica é feita invocando o construtor de A com argumento 1.  Logo, essa variável dinâmica ficará com a sua variável membro A::a com valor 1.
A instrução seguinte invoca funções h() escrevendo o valor devolvido no ecrã.  Como a função A::h() não é virtual, a sua chamada através de ponteiros está sujeita a ligação estática, pelo que a primeira invocação (através de um ponteiro para B) será de B::h() e a segunda (através de um ponteiro para A) será de A::h().
Assim, no ecrã surge primeiro 4 e depois 2.
Finalmente é destruída a variável dinâmica.  A invocação do seu destrutor leva à execução do procedimento B::h(), que por sua vez escreve no ecrã o resultado da chamada de A::h() (por causa da ligação estática), o que faz surgir 1 no ecrã.
[0,5 valores]
Questão 1.3
Quais das seguintes sequências de instruções estão correctas?

 F  A a(5); a.a = 3;

A instrução errada é a.a = 3; porque a, membro da classe A, tem como categoria de acesso protected, logo não pode ser acedido dentro da função main (ver Aula Prática 8).
 F  B* b(1); cout << b->h();
A instrução errada é B* b(1), pois para criar a variável dinâmica para que b deveria apontar deveria ter sido usado o operador new.  A instrução está a tentar atribuir 1 (inteiro) a um ponteiro.
[1 valor]
Questão 1.4
Assuma que as seguintes instruções são dadas dentro da função int B::g() (membro da classe B).  Quais delas estão correctas?

 V  A* p = ba;

ba é do tipo ponteiro para A.
 V  int& r = a;
a é um inteiro e, como a sua categoria de acesso é protected, pode ser acedido nas funções e procedimentos membro da classe B (derivada de A).  Logo está correcta a inicialização de uma referência para um int através de uma variável int.
 V  int* pi = &a;
Tendo definido pi como um ponteiro para int e sendo a categoria de acesso de a protected, pi pode guardar o endereço de a.
 V  A** pp = &ba;
ba é do tipo ponteiro para A, logo definindo pp como um ponteiro para um ponteiro para A, este pode guardar o endereço de um ponteiro para A.
[2 valores]
Questão 2
Num centro de investigação linguística tenciona-se usar um programa de computador para classificar textos que se encontram disponíveis para estudo de fenómenos da Língua Portuguesa.

Pretende-se para isso desenvolver uma hierarquia de classes que represente os vários tipos de textos disponíveis: textos literários, científicos e jornalísticos.

No topo da hierarquia está a classe abstracta TextoEmProsa:

class TextoEmProsa {
  public:
    TextoEmProsa(std::string const& título, std::string const& autor,
          int const número_de_palavras, int const número_de_frases,
          int const número_de_parágrafos)
        : título_(título), autor_(autor),
          número_de_palavras(número_de_palavras),
          número_de_frases(número_de_frases),
          número_de_parágrafos(número_de_parágrafos) {
    }
    TextoEmProsa(std::istream& entrada);
    virtual ~TextoEmProsa() {}

    // Devolve o tipo do texto (literário, científico ou jornalístico):
    virtual string tipo() const = 0;

    // Facilidade de leitura do texto:
    virtual double legibilidade() const {
        return double(número_de_palavras)/número_de_frases;
    }

    // Funções de inspecção:
    std::string const& título() const {
        return título_;
    }
    std::string const& autor() const {
        return autor_;
    }
    int númeroDePalavras() const {
        return número_de_palavras;
    }
    int númeroDeFrases() const {
        return número_de_frases;
    }
    int númeroDeParágrafos() const {
        return número_de_parágrafos;
    }
    // Mostra no ecrã o que é comum a todos os TextoEmProsa:
    virtual void mostra() const;

    // Carrega do canal entrada o que é comum a todos os TextoEmProsa:
    virtual void carrega(std::istream& entrada);

    // Guarda no canal saida o que é comum a todos os TextoEmProsa:
    virtual void guarda(std::ostream& saida) const;

  private:
    std::string título_;
    std::string autor_;
    int número_de_palavras;
    int número_de_frases;
    int número_de_parágrafos;
};

Assuma que o construtor TextoEmProsa(std:::istream& entrada) e os procedimentos mostra(), carrega() e guarda() estão já definidos algures.
Questão 2.1
Defina completamente a classe TextoCientífico que é um texto em prosa e contém a seguinte informação: Esta classe deve dispor de funções inspectoras para a área científica, o número de equações e o título da publicação, e um procedimento modificador para a área científica (caso seja necessário reclassificar o texto).  Devem ainda existir as funções e os procedimentos comuns a todos os textos em prosa.  O tipo de um texto científico é "TextoCientífico".

O índice de legibilidade de um texto científico é dado pelo número médio de equações por parágrafo.

Defina todos os procedimentos e funções da classe TextoCientífico, excepto o construtor que recebe um canal de entrada (istream).

Defina também as classes TextoLiterário e TextoJornalístico, mas não especifique os seus membros, ou seja, não inclua nada entre {}.  Explicite apenas se derivam de alguma outra classe.  I.e., use o formato:

class B /* aqui deve especificar as heranças, se existirem. */ {
    // Aqui não ponha nada!
};
[1,5 valores]
Faz-se uma definição completa.  A resposta necessária é indicada a negrito.

std::string leLinha(std::istream& entrada) {
    std::string linha;
    int c;
    while(entrada.get(c) && c != '\n')
        linha += c;
    return linha;
}

class Data {
  public:
    Data(int const dia = 1, int const mes = 1, int const ano = 1900)
        : dia_(dia), mes_(mes), ano_(ano) {
    }

    int dia() const {
        return dia_;
    }
    int mes() const {
        return mes_;
    }
    int ano() const {
        return ano_;
    }

    void carrega(std::istream& entrada);
    void guarda(std::ostream& saida) const;

  private:
    int dia_, mes_, ano_;
};

ostream& operator <<(ostream& saida, Data const& data);

class TextoEmProsa {
  public:
    TextoEmProsa(std::string const& titulo, std::string const& autor,
                 int const numero_de_palavras, int const numero_de_frases,
                 int const numero_de_paragrafos)
        : titulo_(titulo), autor_(autor),
          numero_de_palavras(numero_de_palavras),
          numero_de_frases(numero_de_frases),
          numero_de_paragrafos(numero_de_paragrafos) {
    }
    TextoEmProsa(std::istream& entrada);
    virtual ~TextoEmProsa() {}

    // Devolve o tipo do texto (literário, científico ou jornalístico):
    virtual string tipo() const = 0;

    // Facilidade de leitura do texto:
    virtual double legibilidade() const {
        return double(numero_de_palavras)/numero_de_frases;
    }

    // Funções de inspecção:
    std::string const& titulo() const {
        return titulo_;
    }
    std::string const& autor() const {
        return autor_;
    }
    int numeroDePalavras() const {
        return numero_de_palavras;
    }
    int numeroDeFrases() const {
        return numero_de_frases;
    }
    int numeroDeParagrafos() const {
        return numero_de_paragrafos;
    }
    // Mostra no ecrã o que é comum a todos os TextoEmProsa:
    virtual void mostra() const;

    // Carrega do canal entrada o que é comum a todos os TextoEmProsa:
    virtual void carrega(std::istream& entrada);

    // Guarda no canal saida o que é comum a todos os TextoEmProsa:
    virtual void guarda(std::ostream& saida) const;

  private:
    std::string titulo_;
    std::string autor_;
    int numero_de_palavras;
    int numero_de_frases;
    int numero_de_paragrafos;
};

TextoEmProsa::TextoEmProsa(std::istream& entrada) {
    carrega(entrada);
}

void TextoEmProsa::mostra() const {
    cout << "Título: " << titulo_ << endl;
    cout << "Autor: " << autor_ << endl;
    cout << "Número de palavras: " << numero_de_palavras << endl;
    cout << "Número de frases: " << numero_de_frases << endl;
    cout << "Número de parágrafos: " << numero_de_paragrafos << endl;
}

void TextoEmProsa::carrega(std::istream& entrada) {
    titulo_ = leLinha(entrada);
    autor_ = leLinha(entrada);
    entrada >> numero_de_palavras;
    entrada >> numero_de_frases;
    entrada >> numero_de_paragrafos;
    leLinha(entrada);   // limpa a última linha em leitura.
}

void TextoEmProsa::guarda(std::ostream& saida) const {
    saida << titulo_ << endl;
    saida << autor_ << endl;
    saida << numero_de_palavras << endl;
    saida << numero_de_frases << endl;
    saida << numero_de_paragrafos << endl;
}

class TextoCientifico : public TextoEmProsa {
  public:
    TextoCientifico(std::string const& titulo, std::string const& autor,
                    int const numero_de_palavras,
                    int const numero_de_frases,
                    int const numero_de_paragrafos,
                    std::string const& area_cientifica,
                    int const numero_de_equacoes,
                    std::string const& titulo_da_publicacao)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
                       numero_de_frases, numero_de_paragrafos),
          area_cientifica(area_cientifica),
          numero_de_equacoes(numero_de_equacoes),
          titulo_da_publicacao(titulo_da_publicacao) {
    }
    TextoCientifico(std::istream& entrada);

    virtual string tipo() const {
        return "TextoCientífico";
    }

    virtual double legibilidade() const {
        return double(numero_de_equacoes) / numeroDeParagrafos();
    }

    string areaCientifica() const {
        return area_cientifica;
    }
    int numeroDeEquacoes() const {
        return numero_de_equacoes;
    }
    string tituloDaPublicacao() const {
        return titulo_da_publicacao;
    }
    virtual void mostra() const;

    void areaCientifica(string const& nova_area) {
        area_cientifica = nova_area;
    }

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    string area_cientifica;
    int numero_de_equacoes;
    string titulo_da_publicacao;

    void carregaPrivado(std::istream& entrada);
};

TextoCientifico::TextoCientifico(std::istream& entrada)
    : TextoEmProsa(entrada) {
    carregaPrivado(entrada);
}

void TextoCientifico::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Área Científica: " << areaCientifica() << endl;
    cout << "Número de equações: " << numeroDeEquacoes() << endl;
    cout << "Título da publicação: " << tituloDaPublicacao() << endl;
}

void TextoCientifico::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    carregaPrivado(entrada);
}

void TextoCientifico::guarda(std::ostream& saida) const {
    TextoEmProsa::guarda(saida);
    saida << area_cientifica;
    saida << numero_de_equacoes;
    saida << titulo_da_publicacao;
}

void TextoCientifico::carregaPrivado(std::istream& entrada) {
    area_cientifica = leLinha(entrada);
    // entrada >> area_cientifica
    entrada >> numero_de_equacoes;
    titulo_da_publicacao = leLinha(entrada);
}

class TextoLiterario : public TextoEmProsa {
  public:
    TextoLiterario(std::string const& titulo, std::string const& autor,
                   int const numero_de_palavras, int const numero_de_frases,
                   int const numero_de_paragrafos,
                   std::string const& critica)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
          numero_de_frases, numero_de_paragrafos), critica_(critica) {
    }
    TextoLiterario(std::istream& entrada);

    virtual string tipo() const {
        return "TextoLiterario";
    }

    string critica() const {
        return critica_;
    }

    void critica(std::string const& nova_critica) {
        critica_ = nova_critica;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    string critica_;
};

TextoLiterario::TextoLiterario(std::istream& entrada)
    : TextoEmProsa(entrada) {
    critica_ = leLinha(entrada);
}

void TextoLiterario::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Crítica: " << critica() << endl;
}

void TextoLiterario::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    critica_ = leLinha(entrada);
}

void TextoLiterario::guarda(std::ostream& saida) const {
    saida << tipo() << endl;
    TextoEmProsa::guarda(saida);
    saida << critica_;
}

class TextoJornalistico : public TextoEmProsa {
  public:
    TextoJornalistico(std::string const& titulo, std::string const& autor,
                      int const numero_de_palavras,
                      int const numero_de_frases,
                      int const numero_de_paragrafos,
                      std::string const& jornal,
                      Data const& data)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
          numero_de_frases, numero_de_paragrafos),
          jornal_(jornal), data_(data) {
    }
    TextoJornalistico(std::istream& entrada);

    virtual string tipo() const {
        return "TextoJornalistico";
    }

    string jornal() const {
        return jornal_;
    }
    Data data() const {
        return data_;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

private:
    string jornal_;
    Data data_;
};

TextoJornalistico::TextoJornalistico(std::istream& entrada)
    : TextoEmProsa(entrada) {
    jornal_ = leLinha(entrada);
    data_.carrega(entrada);
}

void TextoJornalistico::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Jornal: " << jornal() << endl;
    cout << "Data: " << data() << endl;
}

void TextoJornalistico::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    jornal_ = leLinha(entrada);
    data_.carrega(entrada);
}

void TextoJornalistico::guarda(std::ostream& saida) const {
    saida << tipo() << endl;
    TextoEmProsa::guarda(saida);
    saida << jornal_;
    data_.guarda(saida);
}

Questão 2.2
Uma editora posteriormente disponibiliza novos textos, entre os quais se encontravam textos em verso, pelo que  é necessário alterar a hierarquia previamente definida.

Assim, deve ser introduzida uma nova classe abstracta Texto que representa qualquer tipo de texto.  Deve ser ainda criada uma nova classe concreta que represente os novos textos em verso, sabendo que tanto os textos em verso, como os textos em prosa são textos.

Os textos em verso contêm o título, o autor, o número de palavras e o número de estrofes, e dispõem das respectivas funções de inspecção.  O índice de legibilidade de um texto poético é 0 e o tipo é "TextoEmVerso".

Reestruture a hierarquia de classes de modo a acomodar estas alterações.  Defina todas as classes, mas não defina nenhum dos métodos.

Tal como na alínea anterior, não especifique os membros das classes TextoLiterário e TextoJornalístico.

[2 valores]

Questão 2.3
Durante a execução do programa é possível realizar as seguintes operações com a colecção de textos: Defina a classe ColecçãoDeTextos que contém uma lista de ponteiros para os textos que o compõem.  Esta classe deve dispor de funções e procedimentos que realizem as operações permitidas às colecções de texto (referidas acima). Deve ainda existir um destrutor, que será responsável pela destruição das instâncias de classes concretas derivadas da classe Texto para as quais o itens da lista apontam.

Para definir a lista pode usar qualquer das possibilidades referidas durante as aulas: list<Texto*>, ListaPonteiroTexto ou Lista<Texto*>.

Não é necessário implementar nenhum dos métodos nesta alínea.

[1 valor]

Questão 2.4
Defina o destrutor da classe ColecçãoDeTextos.

[1 valor]

Questão 2.5
Defina o procedimento membro da classe ColecçãoDeTextos que remove um texto da colecção, dado o seu título.  Não deve abortar o programa em nenhuma circunstância.  No caso de não existir nenhum texto com o título dado o procedimento não faz nada.

[1,5 valores]

Questão 2.6
Defina a função membro da classe ColecçãoDeTextos que calcula o índice médio de legibilidade da colecção.

[1 valor]

Questão 2.7
Defina o procedimento membro da classe ColecçãoDeTextos que lê textos a partir de um canal.  Admita que os textos lidos têm todos títulos diferentes.

Os dados lidos do canal têm o seguinte formato:

número_de_textos
tipo_do_primeiro_texto
... informação do primeiro texto
tipo_do_segundo_texto
... informação do segundo texto
...
tipo_do_último_texto
... informação do último texto
[1,5 valores]
Questão 2.8
Defina o procedimento membro da classe ColecçãoDeTextos que guarda uma colecção num canal (no formato da alínea anterior).

[1 valor]

Faz-se uma definição completa (mas não são tratados erros de leitura...).  A resposta necessária é indicada a negrito.

std::string leLinha(std::istream& entrada) {
    std::string linha;
    int c;
    while(entrada.get(c) && c != '\n')
        linha += c;
    return linha;
}

class Data {
  public:
    Data(int const dia = 1, int const mes = 1, int const ano = 1900)
        : dia_(dia), mes_(mes), ano_(ano) {
    }

    int dia() const {
        return dia_;
    }
    int mes() const {
        return mes_;
    }
    int ano() const {
        return ano_;
    }

    void carrega(std::istream& entrada);
    void guarda(std::ostream& saida) const;

  private:
    int dia_, mes_, ano_;
};

ostream& operator <<(ostream& saida, Data const& data);

class Texto {
  public:
    Texto(std::string const& titulo, std::string const& autor,
          int const numero_de_palavras)
        : titulo_(titulo), autor_(autor),
          numero_de_palavras(numero_de_palavras) {
    }
    Texto(std::istream& entrada);
    virtual ~Texto() {}

    // Cria um clone:
    virtual Texto* clona() const = 0;

    virtual std::string tipo() const = 0;
    virtual double legibilidade() const = 0;

    std::string const& titulo() const {
        return titulo_;
    }
    std::string const& autor() const {
        return autor_;
    }
    int numeroDePalavras() const {
        return numero_de_palavras;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    std::string titulo_;
    std::string autor_;
    int numero_de_palavras;
};

Texto::Texto(std::istream& entrada) {
    carrega(entrada);
}

void Texto::mostra() const {
    cout << "Título: " << titulo_ << endl;
    cout << "Autor: " << autor_ << endl;
    cout << "Número de palavras: " << numero_de_palavras << endl;
}

void Texto::carrega(std::istream& entrada) {
    titulo_ = leLinha(entrada);
    autor_ = leLinha(entrada);
    entrada >> numero_de_palavras;
    leLinha(entrada); // limpa a última linha em leitura
}

void Texto::guarda(std::ostream& saida) const {
    saida << titulo_ << endl;
    saida << autor_ << endl;
    saida << numero_de_palavras << endl;
}

class TextoEmProsa : public Texto {
  public:
    TextoEmProsa(std::string const& titulo, std::string const& autor,
                 int const numero_de_palavras, int const numero_de_frases,
                 int const numero_de_paragrafos)
        : Texto(titulo, autor, numero_de_palavras),
          numero_de_frases(numero_de_frases),
          numero_de_paragrafos(numero_de_paragrafos) {
    }
    TextoEmProsa(std::istream& entrada);

    // Cria um clone:
    virtual TextoEmProsa* clona() const {
        return new TextoEmProsa(*this);
    }

    virtual string tipo() const = 0;

    virtual double legibilidade() const {
        return double(numeroDePalavras())/numero_de_frases;
    }

    int numeroDeFrases() const {
        return numero_de_frases;
    }
    int numeroDeParagrafos() const {
        return numero_de_paragrafos;
    }
    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    int numero_de_frases;
    int numero_de_paragrafos;

    void carregaPrivado(std::istream& entrada);
};

TextoEmProsa::TextoEmProsa(std::istream& entrada)
    : Texto(entrada) {
    carregaPrivado(entrada);
}

void TextoEmProsa::mostra() const {
    Texto::mostra();
    cout << "Número de frases: " << numero_de_frases << endl;
    cout << "Número de parágrafos: " << numero_de_paragrafos << endl;
}

void TextoEmProsa::carrega(std::istream& entrada) {
    Texto::carrega(entrada);
    carregaPrivado(entrada);
}

void TextoEmProsa::guarda(std::ostream& saida) const {
    Texto::guarda(saida);
    saida << numero_de_frases << endl;
    saida << numero_de_paragrafos << endl;
}

void TextoEmProsa::carregaPrivado(std::istream& entrada) {
    entrada >> numero_de_frases;
    entrada >> numero_de_paragrafos;
    leLinha(entrada);   // limpa a última linha em leitura.
}

class TextoEmVerso : public Texto {
  public:
    TextoEmVerso(std::string const& titulo,
                 std::string const& autor,
                 int const numero_de_palavras,
                 int const numero_de_estrofes)
        : Texto(titulo, autor, numero_de_palavras),
          numero_de_estrofes(numero_de_estrofes) {
    }
    TextoEmVerso(std::istream& entrada);

    // Cria um clone:
    virtual TextoEmVerso* clona() const {
        return new TextoEmVerso(*this);
    }

    virtual string tipo() const {
        return "TextoEmVerso";
    }

    virtual double legibilidade() const {
        return 0.0;
    }
    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    int numero_de_estrofes;
};

TextoEmVerso::TextoEmVerso(std::istream& entrada)
    : Texto(entrada) {
    entrada >> numero_de_estrofes;
    leLinha(entrada);
}

void TextoEmVerso::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    Texto::mostra();
    cout << "Número de estrofes: " << numero_de_estrofes << endl;
}

void TextoEmVerso::carrega(std::istream& entrada) {
    Texto::carrega(entrada);
    entrada >> numero_de_estrofes;
    leLinha(entrada);
}

void TextoEmVerso::guarda(std::ostream& saida) const {
    Texto::guarda(saida);
    saida << numero_de_estrofes << endl;
}

class TextoCientifico : public TextoEmProsa {
  public:
    TextoCientifico(std::string const& titulo,
                    std::string const& autor,
                    int const numero_de_palavras,
                    int const numero_de_frases,
                    int const numero_de_paragrafos,
                    std::string const& area_cientifica,
                    int const numero_de_equacoes,
                    std::string const& titulo_da_publicacao)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
                       numero_de_frases, numero_de_paragrafos),
          area_cientifica(area_cientifica),
          numero_de_equacoes(numero_de_equacoes),
          titulo_da_publicacao(titulo_da_publicacao) {
    }
    TextoCientifico(std::istream& entrada);

    // Cria um clone:
    virtual TextoCientífico* clona() const {
        return new TextoCientifico(*this);
    }

    virtual string tipo() const {
        return "TextoCientífico";
    }

    virtual double legibilidade() const {
        return double(numero_de_equacoes)/numeroDeParagrafos();
    }

    string areaCientifica() const {
        return area_cientifica;
    }
    int numeroDeEquacoes() const {
        return numero_de_equacoes;
    }
    string tituloDaPublicacao() const {
        return titulo_da_publicacao;
    }

    void areaCientifica(std::string const& nova_area) {
        area_cientifica = nova_area;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    string area_cientifica;
    int numero_de_equacoes;
    string titulo_da_publicacao;

    void carregaPrivado(std::istream& entrada);
};

TextoCientifico::TextoCientifico(std::istream& entrada)
    : TextoEmProsa(entrada) {
    carregaPrivado(entrada);
}

void TextoCientifico::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Área Científica: " << areaCientifica() << endl;
    cout << "Número de equações: " << numeroDeEquacoes() << endl;
    cout << "Título da publicação: " << tituloDaPublicacao() << endl;
}

void TextoCientifico::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    carregaPrivado(entrada);
}

void TextoCientifico::guarda(std::ostream& saida) const {
    saida << tipo() << endl;
    TextoEmProsa::guarda(saida);
    saida << area_cientifica;
    saida << numero_de_equacoes;
    saida << titulo_da_publicacao;
}

void TextoCientifico::carregaPrivado(std::istream& entrada) {
    area_cientifica = leLinha(entrada);
    entrada >> numero_de_equacoes;
    titulo_da_publicacao = leLinha(entrada);
}

class TextoLiterario : public TextoEmProsa {
  public:
    TextoLiterario(std::string const& titulo, std::string const& autor,
                   int const numero_de_palavras, int const numero_de_frases,
                   int const numero_de_paragrafos,
                   std::string const& critica)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
                       numero_de_frases, numero_de_paragrafos),
          critica_(critica) {
    }
    TextoLiterario(std::istream& entrada);

    // Cria um clone:
    virtual TextoLiterario* clona() const {
        return new TextoLiterario(*this);
    }

    virtual string tipo() const {
        return "TextoLiterario";
    }

    string critica() const {
        return critica_;
    }

    void critica(std::string const& nova_critica) {
        critica_ = nova_critica;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    string critica_;
};

TextoLiterario::TextoLiterario(std::istream& entrada)
    : TextoEmProsa(entrada) {
    critica_ = leLinha(entrada);
}

void TextoLiterario::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Crítica: " << critica() << endl;
}

void TextoLiterario::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    critica_ = leLinha(entrada);
}

void TextoLiterario::guarda(std::ostream& saida) const {
    saida << tipo() << endl;
    TextoEmProsa::guarda(saida);
    saida << critica_;
}

class TextoJornalistico : public TextoEmProsa {
  public:
    TextoJornalistico(std::string const& titulo, std::string const& autor,
                      int const numero_de_palavras,
                      int const numero_de_frases,
                      int const numero_de_paragrafos,
                      std::string const& jornal,
                      Data const& data)
        : TextoEmProsa(titulo, autor, numero_de_palavras,
                       numero_de_frases, numero_de_paragrafos),
          jornal_(jornal),
          data_(data) {
    }
    TextoJornalistico(std::istream& entrada);

    // Cria um clone:
    virtual TextoJornalistico* clona() const {
        return new TextoJornalistico(*this);
    }

    virtual string tipo() const {
        return "TextoJornalistico";
    }

    string jornal() const {
        return jornal_;
    }
    Data data() const {
        return data_;
    }

    virtual void mostra() const;

    virtual void carrega(std::istream& entrada);
    virtual void guarda(std::ostream& saida) const;

  private:
    string jornal_;
    Data data_;
};

TextoJornalistico::TextoJornalistico(std::istream& entrada)
    : TextoEmProsa(entrada) {
    jornal_ = leLinha(entrada);
    data_.carrega(entrada);
}

void TextoJornalistico::mostra() const {
    cout << "Tipo de texto: " << tipo() << endl;
    TextoEmProsa::mostra();
    cout << "Jornal: " << jornal() << endl;
    cout << "Data: " << data() << endl;
}

void TextoJornalistico::carrega(std::istream& entrada) {
    TextoEmProsa::carrega(entrada);
    jornal_ = leLinha(entrada);
    data_.carrega(entrada);
}

void TextoJornalistico::guarda(std::ostream& saida) const {
    saida << tipo() << endl;
    TextoEmProsa::guarda(saida);
    saida << jornal_;
    data_.guarda(saida);
}

class ColeccaoDeTextos {
  public:
    ColeccaoDeTextos() {};
    ColeccaoDeTextos(ColeccaoDeTextos const& c);
    ~ColeccaoDeTextos();

    void operator = (ColeccaoDeTextos const& c);

    void informacao(std::string const& titulo) const;

    void acrescenta(Texto* t);
    void remove(std::string const& titulo);

    double indiceMedioDeLegibilidade() const;

    void carrega(std::istream& entrada);
    void guarda(std::ostream& saida) const;

  private:
    list<Texto*> textos;
};

ColeccaoDeTextos::ColeccaoDeTextos(ColeccaoDeTextos const& c) {
    for(list<Texto*>::const_iterator i = c.textos.begin();
        i != c.textos.end(); ++i)
        textos.push_back((*i)->clona());
}

ColeccaoDeTextos::~ColeccaoDeTextos() {
    for(list<Texto*>::const_iterator i = textos.begin(); i != textos.end();
        ++i)
        delete *i;
}

void ColeccaoDeTextos::operator =(ColeccaoDeTextos const& c) {
    while(!textos.empty()) {
        delete textos.front();
        textos.pop_front();
    }

    for(list<Texto*>::const_iterator i = c.textos.begin();
        i != c.textos.end(); ++i)
        textos.push_back((*i)->clona());
}

void ColeccaoDeTextos::informacao(std::string const& titulo) const {
    for(list<Texto*>::const_iterator i = textos.begin(); i != textos.end();
        ++i)
        if((*i)->titulo() == titulo) {
            (*i)->mostra();
            return;
        }
}

void ColeccaoDeTextos::acrescenta(Texto* t) {
    textos.push_back(t);
}

void ColeccaoDeTextos::remove(std::string const& titulo) {
    for(list<Texto*>::iterator i = textos.begin(); i != textos.end(); ++i)
        if((*i)->titulo() == titulo) {
            delete *i;
            textos.erase(i);
            return;
        }
}

double ColeccaoDeTextos::indiceMedioDeLegibilidade() const {
    double total = 0.0;

    for(list<Texto*>::const_iterator i = textos.begin(); i != textos.end();
        ++i)
        total += (*i)->legibilidade();

    return total / textos.size();
}

void ColeccaoDeTextos::carrega(std::istream& entrada) {
    int numero_de_textos;
    entrada >> numero_de_textos;

    for(int i = 0; i != numero_de_textos; ++i) {
        std::string tipo_de_texto;
        entrada >> tipo_de_texto;

        if(tipo_de_texto == "TextoEmVerso")
            textos.push_back(new TextoEmVerso(entrada));
        else if (tipo_de_texto == "TextoCientífico")
            textos.push_back(new TextoCientifico(entrada));
        else if (tipo_de_texto == "TextoLiterário")
            textos.push_back(new TextoLiterario(entrada));
        else if (tipo_de_texto == "TextoJornalístico")
            textos.push_back(new TextoJornalistico(entrada));
        else {
            cerr << "Erro: tipo inválido." << endl;
            exit(1);
        }
    }
}

void ColeccaoDeTextos::guarda(std::ostream& saida) const {
    saida << textos.size() << endl;
    for(list<Texto*>::const_iterator i = textos.begin(); i != textos.end();
        ++i) {
        saida << (*i)->tipo() << endl;
        (*i)->guarda(saida);
    }
}

Questão 3
Considere a seguinte estrutura
struct Elo {
    typedef int Item;

    Item item;
    Elo* anterior;
    Elo* seguinte;
};

usada para representar os elos de uma cadeia duplamente ligada com a particularidade de ser circular, i.e., o elo seguinte do último elo da cadeia é o primeiro elo da cadeia e o elo anterior do primeiro elo da cadeia é o último elo da cadeia.  A cadeia não tem guardas.

Considere que existe um procedimento

inline void troca(Elo*& a, Elo*& b) {
    Elo* aux = a;
    a = b;
    b = aux;
}
que troca dois ponteiros para Elo.

Defina um procedimento void inverte(Elo*& primeiro) que inverta a ordem dos elos de uma cadeia duplamente ligada e circular cujo primeiro elo é apontado pelo ponteiro primeiro.  Note que quando a cadeia está vazia o ponteiro para o primeiro elemento tem o valor 0.  Note também que em geral o primeiro elo da cadeia passa a ser o que antes era o último.

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

[1,5 valores]

Apresenta-se um programa completo, com o qual é possível testar o algoritmo.  A negrito a resposta necessária.

#include <iostream>

using namespace std;

struct Elo {
    typedef int Item;

    Item item;
    Elo* anterior;
    Elo* seguinte;
    Elo(Item const& item, Elo* anterior = 0, Elo* seguinte = 0)
        : item(item), anterior(anterior), seguinte(seguinte) {
    }
};

inline void troca(Elo*& a, Elo*& b) {
    Elo* aux = a;
    a = b;
    b = aux;
}

void poeUltimo(Elo*& primeiro, Elo::Item const& item) {
    if(primeiro == 0) {
        primeiro = new Elo(item);
        primeiro->seguinte = primeiro;
        primeiro->anterior = primeiro;
    } else
        primeiro->anterior = primeiro->anterior->seguinte =
            new Elo(item, primeiro->anterior, primeiro);
}

void mostra(Elo* primeiro) {
    if(primeiro != 0) {
        Elo* elo = primeiro;
        do {
            cout << elo->item << endl;
            elo = elo->seguinte;
        } while(elo != primeiro);
    }
}

void inverte(Elo*& primeiro) {
    if(primeiro != 0) {
        Elo* elo = primeiro = primeiro = primeiro->anterior;
        do {
            troca(elo->anterior, elo->seguinte);
            elo = elo->seguinte;
        } while(elo != primeiro);
    }

}

int main()
{
    cout << "Cadeia com 0 itens: " << endl;
    Elo* primeiro0 = 0;
    mostra(primeiro0);
    inverte(primeiro0);
    mostra(primeiro0);

    cout << "Cadeia com 1 itens: " << endl;
    Elo* primeiro1 = 0;
    poeUltimo(primeiro1, 1);
    mostra(primeiro1);
    inverte(primeiro1);
    mostra(primeiro1);

    cout << "Cadeia com 2 itens: " << endl;
    Elo* primeiro2 = 0;
    poeUltimo(primeiro2, 1);
    poeUltimo(primeiro2, 2);
    mostra(primeiro2);
    inverte(primeiro2);
    mostra(primeiro2);

    cout << "Cadeia com 3 itens: " << endl;
    Elo* primeiro3 = 0;
    poeUltimo(primeiro3, 1);
    poeUltimo(primeiro3, 2);
    poeUltimo(primeiro3, 3);
    mostra(primeiro3);
    inverte(primeiro3);
    mostra(primeiro3);
}

Questão 4
Considere a seguinte definição de uma pilha de inteiros:
class PilhaInt {
  public:
    typedef int Item;

    PilhaInt(int tamanho_inicial = tamanho_minimo);  // construtor da classe.
    PilhaInt(PilhaInt const& p);                     // construtor por cópia da classe.
    ~PilhaInt();                                     // destrutor da classe.

    PilhaInt& operator = (PilhaInt const& p);        // atribuição por cópia da classe.

    ...

    bool vazia() const;         // devolve true sse a pilha estiver vazia.
    bool cheia() const;         // devolve true sse a pilha estiver cheia.
    int altura() const;         // devolve o número de itens na pilha.

    ...

 private:
    static int const tamanho_minimo = 32; // tamanho mínimo da matriz (tem de ser > 0).

    mutable int tamanho;        // tamanho corrente da matriz.
    int quantos;                // número de itens actualmente na pilha.
    mutable Item* itens;        // matriz dinâmica dos itens.
};

Defina a função bool PilhaInt::cheia() const.  Uma pilha está cheia quando não havendo mais elementos disponíveis na matriz dinâmica, se tenta duplicar o tamanho desta matriz e tal não é possível por ter sido lançada uma excepção.

Observação: a excepção lançada pelo operador new em caso de falta de memória é bad_alloc.  Mas é preferível lidar com o problema para qualquer excepção.

Defina as funções e procedimentos (membros ou não) que achar necessários.

[2 valores]

Ver Resolução da Aula Prática 6.
Questão 5
Defina (distinguindo) ligação estática de ligação dinâmica.  Relacione esta distinção com o conceito de polimorfismo.

[1 valor]

Ver Aula Prática 8.