Arabian Nights in C++

From Wiki**3

The Problem (in Portuguese)

Aplicando os conceitos OO que já conhece, concretize as classes necessárias para representar a funcionalidade que se descreve de seguida. Pode ser necessária a criação de classes adicionais não descritas abaixo. Todas as classes indicadas devem pertencer ao namespace arabiannights.

Funcionalidade da lâmpada mágica (classe MagicLamp)

Uma lâmpada mágica liberta génios quando esfregada (método rub). Os génios podem ser bem ou mal-humorados. O humor dos génios é determinado pelas condições da lâmpada: sempre que a lâmpada tiver sido esfregada um número par de vezes (sem contar a actual), o génio sai mal-humorado. A quantidade de génios disponíveis é determinada no momento de encantamento da lâmpada (criação). Depois de esgotados os génios disponíveis, já não adianta esfregar a lâmpada para obter um génio, bem ou mal-humorado: nestas condições, a lâmpada cria um pequeno demónio que responde a pedidos de forma literal mas perversa. Devido a requisitos de sustentabilidade ambiental, as normas de produção exigem que as lâmpadas sejam recarregáveis. Assim, é possível voltar a obter génios quando se esfrega a lâmpada (em número igual ao inicial). O processo de recarregamento exige apenas que um demónio seja alimentado à lâmpada (método feedDemon).

Quando se cria uma nova lâmpada é necessário indicar a quantidade inicial de génios que é possível invocar (a lâmpada cria internamente os génios e apenas retorna referências ou ponteiros para eles). É possível saber quantos génios ainda estão disponíveis na lâmpada (método nGenies). É ainda possível saber quantas vezes a lâmpada já foi recarregada (método nDemons). Quando se esfrega a lâmpada, deve-se indicar quantos desejos se espera que o génio realize (independentemente de o génio os poder negar).

Deve ser possível comparar duas instâncias da classe MagicLamp através do operador ==. Considera-se que duas lâmpadas são iguais se tiverem a mesma capacidade e se os valores retornados pelos métodos nGenies e nDemons forem iguais.

Nota: a lâmpada liberta apenas um génio de cada vez.

Funcionalidade do génio bem-humorado (classe FriendlyGenie)

O génio bem-humorado concede todos os desejos que lhe forem colocados (método grantWish e retorno true), até ao limite com que foi chamado da lâmpada. Depois do limite já não são concedidos desejos (retorno false). É possível saber quantos desejos já foram concedidos (método nGrantedWishes) e quantos ainda existem disponíveis (método nRemainingWishes).

Nota: o génio concede apenas um desejo de cada vez.

Funcionalidade do génio mal-humorado (classe GrumpyGenie)

O génio mal-humorado concede apenas o primeiro desejo que lhe for colocado (método grantWish e retorno true), independentemente do limite com que foi chamado da lâmpada (retorno false após o primeiro). É possível saber se o desejo já foi realizado (método nGrantedWishes retorna 1).

Funcionalidade do demónio reciclável (classe RecyclableDemon)

O demónio concede todos os desejos que lhe forem colocados (método grantWish e retorno true), independentemente do limite com que foi chamado da lâmpada. Se o demónio for recolocado na lâmpada (para a recarregar), já não pode realizar mais desejos (retorno false). É possível saber quantos desejos já foram concedidos (método nGrantedWishes).

Nota: o demónio concede apenas um desejo de cada vez.

Observações

Todas as classes devem ter métodos de acesso (get e set) (quando apropriado) para os seus atributos.

O operador <<, aplicado aos génios e ao demónio, deve enviar para o stream de saída correspondente uma das seguintes cadeias de caracteres:

  • Friendly genie has granted # wishes and still has # to grant. (# representam os contadores apropriados)
  • Grumpy genie has granted a wish. / Grumpy genie has a wish to grant. (consoante já concedeu ou não o pedido)
  • Recyclable demon has granted # wishes. / Demon has been recycled. (antes e depois de recarregar uma lâmpada)

A função main deve executar a seguinte sequência de operações:

  1. Criar uma lâmpada mágica com capacidade para 4 génios.
  2. Esfregar 5 vezes a lâmpada, indicando os números de desejos 2, 3, 4, 5, 1.
  3. Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
  4. Pedir um desejo a cada um dos génios.
  5. Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
  6. Pedir um desejo a cada um dos génios.
  7. Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
  8. Colocar o demónio reciclável na lâmpada.
  9. Esfregar a lâmpada, indicando 7 como número de desejos.
  10. Imprimir em std::cout (utilizando o operador <<) o génio obtido.

Implementation

The following corresponds to a possible telling of a special version of the Arabian Nights stories. In any implementation, the following concepts will exist in one form or another: MagicLamp, Genie (a general type of genie, which may also describe the recyclable demon), FriendlyGenie, GrumpyGenie, and RecyclableDemon.

In the following implementation, each class will be defined in a pair of files: a header file (.h) and a file containing method definitions (.cpp). Although C++ does not enforce (or even needs) this division, it is useful for managing complexity and makes code understanding easier. Thus, the following files will exist:

  • For the magic lamp: MagicLamp.h (interface) and MagicLamp.cpp (implementation).
  • For the genies: Genie.h; FriendlyGenie.h; GrumpyGenie.h; and RecyclableDemon.h (we did not use .cpp file because these classes are small).
  • For the main function: main.cpp.

Note that the namespace has not been taken into account for defining class names. Nevertheless, when organizing large projects, especially if multiple namespaces exist, it may be advisable to establish a parallel between namespaces and directories (much like packages in Java).

Class MagicLamp

File MagicLamp.h contains the interface and a few small functions:

Ficheiro MagicLamp.h
{{{2}}}

File MagicLamp.cpp contains the implementations of the functions not defined in the header (.h) file. Note how the destructor destroys all the genies and the demon when the lamp itself is destroyed (the only magic lamp is created in the stack of the main function - below - and its destructor is called at the end of the main function, when its stack is released).

Ficheiro MagicLamp.cpp
{{{2}}}

Class Genie

Class Genie defines the basic behaviour for all genies. The whole class is defined in the header file (Genie.h). Notice the two versions (one is commented out) of the output operator.

Note that the output operator by itself if not capable of handling the polymorphic access used in the main function (because it is used as an argument). For it to work correctly, we defined an abstract method (dumpTo) that each class must implement. This method will be correctly called in polymorphic situations.

Note also that, since Genie is a class whose methods are to be redefined by subclasses and whose pointers will only point to subclass instances, needs a virtual destructor: in this was, the delete operator will look at the actual type of the object (instead of using the type of the pointer), allowing the correct destructor (that of the object's class and not the pointer's) to be called.

Ficheiro Genie.h
{{{2}}}

Class FriendlyGenie

A friendly genie is almost like a general genie (it differs in the output messages). Since it is a small class, it is completely defined in the header file (FriendlyGenie.h).

Ficheiro FriendlyGenie.h

<cpp>

  1. ifndef __ARABIANNIGHTS_FRIENDLYGENIE_H__
  2. define __ARABIANNIGHTS_FRIENDLYGENIE_H__
  1. include <iostream>
  2. include "Genie.h"

namespace arabiannights {

 /**
  * A friendly genie is actually a normal genie, but with special
  * descriptive text.
  */
 class FriendlyGenie : public Genie {
 public:
   /**
    * @param limit the maximum number of wishes to grant.
    */
   FriendlyGenie(int limit) : Genie(limit) {}
   /**
    * Redefined from Genie.
    * @return the number of wishes left to grant.
    */
   inline int nRemainingWishes() const {
     return getLimit() - nGrantedWishes();
   }
   /**
    * Dump to output stream.
    * @param o the output stream.
    */
   void dumpTo(std::ostream &o) const {
     o << "Friendly genie has granted " << nGrantedWishes()
       << " wishes and still has " << nRemainingWishes() << " to grant.";
   }
   /**
    * Output operator.
    * Declared friend: see alternative below (after class).
    */
   friend std::ostream &operator<<(std::ostream &o, const FriendlyGenie &fg) {
     fg.dumpTo(o);
     return o;
   }
 };

} // namespace arabiannights

  1. if 0

// alternative definition for output operator (non-friend) inline std::ostream &operator<<(std::ostream &o, const FriendlyGenie &fg) {

 fg.dumpTo(o);
 return o;

}

  1. endif
  1. endif

</cpp>

Class GrumpyGenie

Like the other previous genie classes, this one is also very small and completely defined in the header file (GrumpyGenie.h):

Ficheiro GrumpyGenie.h
{{{2}}}

Class RecyclableDemon

The RecyclableDemon is a genie that implements some of the control methods in a special way (but still adheres to the template method defined by class Genie). Like the other small classes, it is completely define in RecyclableDemon.h:

Ficheiro RecyclableDemon.h
{{{2}}}

Main Function

In this case, the main is, straightforward. Some aspects merit attention: since the magic lamp will not be used anywhere else, it is a good ideia to create in within the context of the main function, i.e., on its stack, and not on the heap (as would be the case if it were created with the "new" operator).

Função principal (main)
{{{2}}}

Compiling and Running

The following commands are needed to compile the application (note that it suffices to compile the .cpp files).

 g++ -ansi -pedantic -Wall -c MagicLamp.cpp
 g++ -ansi -pedantic -Wall -c main.cpp
 g++ -o an MagicLamp.o main.o

The result of running the an application is the following:

 Grumpy genie has a wish to grant.
 Friendly genie has granted 0 wishes and still has 3 to grant.
 Grumpy genie has a wish to grant.
 Friendly genie has granted 0 wishes and still has 5 to grant.
 Recyclable demon has granted 0 wishes.
 Grumpy genie has granted a wish.
 Friendly genie has granted 1 wishes and still has 2 to grant.
 Grumpy genie has granted a wish.
 Friendly genie has granted 1 wishes and still has 4 to grant.
 Recyclable demon has granted 1 wishes.
 Grumpy genie has granted a wish.
 Friendly genie has granted 2 wishes and still has 1 to grant.
 Grumpy genie has granted a wish.
 Friendly genie has granted 2 wishes and still has 3 to grant.
 Recyclable demon has granted 2 wishes.
 Grumpy genie has a wish to grant.

See Also