#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) { if (aditional > 0) { _content += aditional; } } // Retira um tanto do reservatorio, se existir. // Se nao existir, retira o que tem. // So retira se positivo. double take(double how_many) { if (how_many > 0) { if (_content >= how_many) { _content -= how_many; return how_many; } else { auto res = _content; _content = 0; return res; } } else { return 0; } } }; // 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) : Reservoir(initial) { _limit = limit > 0 ? limit : content(); if (content() > limit) { take(content() - limit); } } // O put precisa ser alterado, para garantir que o // limite nao e excedido. void put(double aditional) override { if (aditional > 0) { if (aditional + content() <= _limit) { Reservoir::put(aditional); } else { Reservoir::put(_limit - content()); } } } }; void por_valor(Reservoir r, double x) { r.put(x); std::cout << "Em por_valor: " << r.content() << std::endl; } void por_referencia(Reservoir &r, double x) { r.put(x); std::cout << "Em por_referencia: " << r.content() << std::endl; } void por_ponteiro(Reservoir *r, double x) { r->put(x); std::cout << "Em por_ponteiro: " << r->content() << std::endl; } 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()); double x{0.5}; int i{0}; std::cout << "r1 tem " << r1.content() << ", v[" << i << "] tem " << v[i]->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 << "Tirei " << 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 tem " << r1.content() << ", v[" << i << "] tem " << v[i]->content() << std::endl; i = i < 2 ? 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 << "Antes de por_valor: " << r2.content() << std::endl; por_valor(r2, 100); std::cout << "Depois de por_valor: " << 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 << "Antes de por_referencia: " << r2.content() << std::endl; por_referencia(r2, 1000); std::cout << "Depois de por_valor: " << 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 << "Antes de por_ponteiro: " << r2.content() << std::endl; por_ponteiro(&r2, 10000); std::cout << "Depois de por_ponteiro: " << r2.content() << std::endl; return 0; }