#include #include #include // Classe para representar um reservatorio class Reservoir { double _content; // Quanto tem guardado. public: // Construtor: Inicializa o reservatorio. // Garante que quantidade >= 0. Reservoir(double initial = 0) { _content = initial > 0 ? initial : 0; } // Retorna o quanto o reservatorio tem no momento double content() const { return _content; } // Insere conteudo no reservatorio. // So adiciona se for positivo. // O "virtual" aqui diz que o metodo deve ser adaptado // em tempo de execucao ao tipo do objeto, de acordo com // a heranca (veja abaixo). virtual void put(double aditional); // Retira um tanto do reservatorio, se existir. // Se nao existir, retira o que tem. // So retira se how_many positivo. // Retorna o quanto retirou. double take(double how_many); virtual ~Reservoir() {} }; // Esta classe usa HERANCA! // LimitedReservoir e classe derivada de Reservoir. // Tambem, Reservoir e a classe base de LimitedReservoir. // Significa que tem tudo o que tem em Reservoir, // mais o que for explicitamente indicado. // Neste caso, tem adicionalmente um campo para guardar // o limite do reservatorio e tem um metodo put alterado // para nao deixar passar do limite. class LimitedReservoir : public Reservoir { double _limit; // Qual o limite do reservatorio. public: // Construtor agora precisa tambem do limite. // O valor inicial e passado para o construtor de // Reservoir (classe base), na lista de inicializacao // de membros. // Garante que o limite >= 0. LimitedReservoir(double limit, double initial = 0); // O put precisa ser alterado, para garantir que o // limite nao e excedido. void put(double aditional) override; }; class Location { double _long, _lat; public: Location(double longitude, double latitude) : _long(longitude), _lat(latitude) {} double longitude() const { return _long; } double latitude() const { return _lat; } }; class LocatedLimitedReservoir : public LimitedReservoir, public Location { public: LocatedLimitedReservoir(double limit, double initial, double longitude, double latitude) : LimitedReservoir(limit, initial), Location(longitude, latitude) {} LocatedLimitedReservoir(double limit, double longitude, double latitude) : LocatedLimitedReservoir(limit, 0, longitude, latitude) {} }; // Atua em r passado por valor. void by_value(Reservoir r, double x); // Atua em r passado por referência. void by_reference(Reservoir &r, double x); // Atua em r passado por ponteiro. void by_pointer(Reservoir *r, double x); // Demonstrando o uso de herança e polimorfismo. int main(int, char *[]) { // Um vetor de ponteiros para classe base. std::vector> v; // Um objeto da classe derivada LimitedReservoir r1{20, 20}; // No vetor de ponteiros para classe base // podemos tambem guardar ponteiros para a // classe derivada. v.push_back(std::make_unique()); v.push_back(std::make_unique(5, 0.33)); v.push_back(std::make_unique()); v.push_back(std::make_unique(3, 1.5, 45.2, -22.4)); double x{0.5}; int i{0}; std::cout << "r1 has " << r1.content() << std::endl; while (r1.content() > 0) { // Quando fazemos a operação take, // executamos sempre o Reservoir::take. auto y = r1.take(x); std::cout << "Taken " << y << std::endl; // Quando executamos put, como ele e virtual, // executamos o Reservoir::put ou o // LimitedReservoir::put, de acordo com o objeto // apontado. // Isso e denominado "polimorfismo". v[i]->put(y); std::cout << "r1 has " << r1.content() << ", v[" << i << "] has " << v[i]->content() << std::endl; i = i < 3 ? i + 1 : 0; x += 1.0; } // O polimorfismo funciona para ponteiros e referencias. LimitedReservoir r2{10}; // Um reservatorio limitado em 10. // Ao passar por valor, e feita uma copia para uma nova // variavel, e a nova variavel vai se comportar de acordo // com a sua definicao. E permitido copiar de classe derivada // para classe base. std::cout << "Before by_value: " << r2.content() << std::endl; by_value(r2, 100); std::cout << "After by_value: " << r2.content() << std::endl; // Ao passar por referencia, a referencia para classe base // aceita referenciar um objeto de classe derivada. // Os metodos "virtual" se adaptam ao tipo do objeto. std::cout << "Before by_reference: " << r2.content() << std::endl; by_reference(r2, 1000); std::cout << "After by_reference: " << r2.content() << std::endl; r2.take(10); // Vamos tirar o que havia sido acrescentado. // Por ponteiro o comportamento e similar a por referencia. std::cout << "Before by_pointer: " << r2.content() << std::endl; by_pointer(&r2, 10000); std::cout << "After by_pointer: " << r2.content() << std::endl; if (auto lrp = dynamic_cast(v[3].get())) { std::cout << "Reservatorio localizado em (" << lrp->longitude() << ", " << lrp->latitude() << ")\n"; } return 0; } //============================================================================== // Métodos da classe Reservoir. // Insere conteudo no reservatorio. // So adiciona se for positivo. void Reservoir::put(double aditional) { if (aditional > 0) { _content += aditional; } } // Retira um tanto do reservatorio, se existir. // Se nao existir, retira o que tem. // So retira se how_many positivo. // Retorna o quanto retirou. double Reservoir::take(double how_many) { double taken{0}; // Quanto foi tirado do reservatório if (how_many > 0) { if (_content >= how_many) { _content -= how_many; taken = how_many; } else { taken = _content; _content = 0; } } return taken; } //============================================================================== // Métodos da classe LimitedReservoir. // Garante que o limite >= 0. LimitedReservoir::LimitedReservoir(double limit, double initial) : Reservoir(initial) { _limit = limit > 0 ? limit : content(); // Garante que conteúdo inicial não é excessivo. if (content() > limit) { take(content() - limit); } } // O put precisa garantir que o limite nao e excedido. void LimitedReservoir::put(double aditional) { if (aditional > 0) { if (aditional + content() <= _limit) { Reservoir::put(aditional); } else { Reservoir::put(_limit - content()); } } } //============================================================================== // Funções auxiliares para o main. // Atua em r passado por valor. void by_value(Reservoir r, double x) { r.put(x); std::cout << "Inside by_value: " << r.content() << std::endl; } // Atua em r passado por referência. void by_reference(Reservoir &r, double x) { r.put(x); std::cout << "Inside by_reference: " << r.content() << std::endl; } // Atua em r passado por ponteiro. void by_pointer(Reservoir *r, double x) { r->put(x); std::cout << "Inside by_pointer: " << r->content() << std::endl; }