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:
- Criar uma lâmpada mágica com capacidade para 4 génios.
- Esfregar 5 vezes a lâmpada, indicando os números de desejos 2, 3, 4, 5, 1.
- Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
- Pedir um desejo a cada um dos génios.
- Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
- Pedir um desejo a cada um dos génios.
- Imprimir em std::cout (utilizando o operador <<) cada um dos génios.
- Colocar o demónio reciclável na lâmpada.
- Esfregar a lâmpada, indicando 7 como número de desejos.
- Imprimir em std::cout (utilizando o operador <<) o génio obtido.
Implementation
The following corresponds to a possible implementation 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:
<cpp>
#ifndef __ARABIANNIGHTS_MAGICLAMP_H__ #define __ARABIANNIGHTS_MAGICLAMP_H__ #include <vector> #include "Genie.h" #include "FriendlyGenie.h" #include "GrumpyGenie.h" #include "RecyclableDemon.h" namespace arabiannights { class Genie; // just a declaration class RecyclableDemon; // just a declaration class MagicLamp { /** Maximum number of wishes (does not mean that the genie will grant them all). */ int _limit; /** Total number of rubs. */ int _totalRubs; /** Number of rubs since last charge. */ int _rubs; /** Number of recharges. */ int _demons; /** Keep track of genies. */ std::vector<Genie*> _genies; /** Keep track of demon. */ RecyclableDemon *_demon; public: MagicLamp(int limit) : _limit(limit), _totalRubs(0), _rubs(0), _demons(0), _demon(NULL) {} /** * Destructor should be virtual (we have other virtual functions), even * though we do not have any subclasses of MagicLamp. */ virtual ~MagicLamp(); int getLimit() const { return _limit; } int getRubs() const { return _rubs; } int getTotalRubs() const { return _totalRubs; } int nGenies() const { return getLimit() - getRubs(); } int nDemons() const { return _demons; } /** * Ask the lamp for a genie. * If the number of rubs is even, return a grumpy genie. * If the number of rubs is more than the limit, return a demon. * Otherwise, return a normal friendly genie. * @return a genie. */ virtual Genie *rub(int wishes); /** * Recharge the magic lamp by feeding it a demon. * A demon can only be used if not previously recycled. * @param demon the recyclable demon. */ virtual void feedDemon(RecyclableDemon *demon); /** * Compare this lamp with another. * @return true if, in addition to the same capacity, the number * of available genies and the number of recharges are the same; * false, otherwise. */ inline bool operator==(const MagicLamp &l) { return getLimit() == l.getLimit() && nGenies() == l.nGenies() && nDemons() == l.nDemons(); } }; } // namespace arabiannights #endif
</cpp>
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).
<cpp>
#include "MagicLamp.h" arabiannights::MagicLamp::~MagicLamp() { if (_demon) delete _demon; for (size_t gx = 0; gx < _genies.size(); gx++) delete _genies[gx]; } /** * Ask the lamp for a genie. * If the number of rubs is even, return a grumpy genie. * If the number of rubs is more than the limit, return a demon. * Otherwise, return a normal friendly genie. * @return a genie. */ arabiannights::Genie *arabiannights::MagicLamp::rub(int wishes) { Genie *g; if (_rubs < _limit) { _rubs++; _totalRubs++; if (_totalRubs % 2 == 0) { g = new FriendlyGenie(wishes); _genies.push_back(g); // remember we created the genie return g; // return genie to the caller } Genie *g = new GrumpyGenie(wishes); _genies.push_back(g); // remember we created the genie return g; // return genie to the caller } return _demon = (_demon ? _demon : new RecyclableDemon(wishes)); } /** * Recharge the magic lamp by feeding it a demon. * A demon can only be used if not previously recycled. * Note that previously create genies ARE NOT deleted * (they may still be active). * @param demon the recyclable demon. */ void arabiannights::MagicLamp::feedDemon(arabiannights::RecyclableDemon *demon) { if (demon != _demon) return; // only accept our demon... if (!demon->recycled()) { demon->recycle(); delete _demon; // zap the lamp's demon and reset lamp _demon = NULL; _rubs = 0; _demons++; } }
</cpp>
Class Genie
Class FriendlyGenie
Class GrumpyGenie
Class RecyclableDemon
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).
#include <iostream> #include "MagicLamp.h" namespace arabiannights { class Genie; class RecyclableDemon; } int main() { // 1. Criar uma lâmpada mágica com capacidade para 4 génios. // Magic lamp is created in the stack. arabiannights::MagicLamp ml(4); // Items 2. through 7. are simply executed in a cycle. int wishes[] = { 2, 3, 4, 5, 1 }; // number of wishes per genie arabiannights::Genie *genies[5]; // vector for genies // 2. Esfregar 5 vezes a lâmpada, indicando os números de desejos 2, 3, 4, 5, 1. for (size_t gx = 0; gx < 5; gx++) genies[gx] = ml.rub(wishes[gx]); // 3. Imprimir em std::cout (utilizando o operador <<) cada um dos génios. for (size_t gx = 0; gx < 5; gx++) std::cout << *genies[gx] << std::endl; // 4. Pedir um desejo a cada um dos génios. for (size_t gx = 0; gx < 5; gx++) genies[gx]->grantWish(); // 5. Imprimir em std::cout (utilizando o operador <<) cada um dos génios. for (size_t gx = 0; gx < 5; gx++) std::cout << *genies[gx] << std::endl; // 6. Pedir um desejo a cada um dos génios. for (size_t gx = 0; gx < 5; gx++) genies[gx]->grantWish(); // 7. Imprimir em std::cout (utilizando o operador <<) cada um dos génios. for (size_t gx = 0; gx < 5; gx++) std::cout << *genies[gx] << std::endl; // 8. Colocar o demónio reciclável na lâmpada. // tricky (how can we be certain the demon is in the 5th position?) ml.feedDemon((arabiannights::RecyclableDemon*)genies[4]); // 9. Esfregar a lâmpada, indicando 7 como número de desejos. arabiannights::Genie *g = ml.rub(7); // 10. Imprimir em std::cout (utilizando o operador <<) o génio obtido. std::cout << *g << std::endl; return 0; }
Compiling and Running
The following commands are needed to compile the application (note that it only necessary 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 1 wishes. Grumpy genie has a wish to grant.