Pular para conteúdo

Lista 11 · Polimorfismo e sobrecarga de operadores

Exercícios da Semana 13 — Polimorfismo e sobrecarga de operadores.

Como resolver

Copie o bloco de código de cada exercício, cole no seu compilador online, preencha a parte marcada como ESCREVA SUA SOLUCAO AQUI e rode. A saída mostra quais testes passaram.

ex01

Dada a classe abstrata Ave (método virtual puro som() const, já declarado), complete Papagaio (deve retornar "Ola!") e Coruja (deve retornar "Uhu!"), ambas sobrescrevendo som().

Papagaio p;
Coruja c;
Ave& a1 = p;
Ave& a2 = c;
a1.som();   // -> "Ola!"   (chamada polimorfica -- via referencia de Ave)
a2.som();   // -> "Uhu!"
// ex01 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX01_H
#define EX01_H

class Ave {
public:
    virtual std::string som() const = 0;
    virtual ~Ave() {}
};

class Papagaio : public Ave {
public:
    std::string som() const override;
};

class Coruja : public Ave {
public:
    std::string som() const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string Papagaio::som() const {
    // TODO: retorne "Ola!"
    return "";
}

std::string Coruja::som() const {
    // TODO: retorne "Uhu!"
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Papagaio p;
    Coruja c;
    Ave& a1 = p;
    Ave& a2 = c;
    assert(a1.som() == std::string("Ola!"));
    assert(a2.som() == std::string("Uhu!"));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex02

Complete as classes Bicicleta e Carro, que herdam da classe abstrata MeioDeTransporte (método virtual puro tempo_de_viagem(double km) const, que devolve o tempo de viagem em horas). Os construtores já estão implementados; implemente apenas tempo_de_viagem() em cada uma.

  • Bicicleta guarda uma velocidade_media_ em km/h. O tempo é simplesmente km / velocidade_media_.
  • Carro guarda uma velocidade_media_ e um minutos_de_parada_ a cada 100 km (parada para abastecer, descansar). O tempo é km / velocidade_media_ mais as paradas: a cada 100 km completos há uma parada. Em 250 km há duas paradas (aos 100 e aos 200 km), e as paradas entram em horas — divida os minutos por 60.0.

Repare que as duas classes respondem à mesma pergunta com regras bem diferentes, e que quem chama tempo_de_viagem() por uma referência MeioDeTransporte& não precisa saber qual das duas está do outro lado. É esse o ponto do método virtual puro: a classe base define a pergunta, cada derivada define a resposta.

Para contar as paradas, use divisão inteira: static_cast<int>(km) / 100.

Bicicleta b(20.0);          // 20 km/h
b.tempo_de_viagem(50.0);    // -> 2.5   (50 / 20)

Carro c(100.0, 30);         // 100 km/h, 30 min de parada a cada 100 km
c.tempo_de_viagem(250.0);   // -> 3.5   (250/100 = 2.5 h, mais 2 paradas = 1.0 h)
// ex02 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX02_H
#define EX02_H

class MeioDeTransporte {
public:
    // Tempo de viagem, em horas, para percorrer a distancia dada em km.
    virtual double tempo_de_viagem(double km) const = 0;
    virtual ~MeioDeTransporte() {}
};

class Bicicleta : public MeioDeTransporte {
private:
    double velocidade_media_;      // km/h

public:
    explicit Bicicleta(double velocidade_media) : velocidade_media_(velocidade_media) {}
    double tempo_de_viagem(double km) const override;
};

class Carro : public MeioDeTransporte {
private:
    double velocidade_media_;      // km/h
    int minutos_de_parada_;        // a cada 100 km completos

public:
    Carro(double velocidade_media, int minutos_de_parada)
        : velocidade_media_(velocidade_media), minutos_de_parada_(minutos_de_parada) {}
    double tempo_de_viagem(double km) const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double Bicicleta::tempo_de_viagem(double km) const {
    // TODO: retorne km / velocidade_media_.
    return 0.0;
}

double Carro::tempo_de_viagem(double km) const {
    // TODO: comece com km / velocidade_media_.
    // Some as paradas: static_cast<int>(km) / 100 paradas, cada uma
    // custando minutos_de_parada_ / 60.0 horas.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
static bool perto(double a, double b) {
    return std::fabs(a - b) < 0.0001;
}

int main() {
    Bicicleta b(20.0);
    assert(perto(b.tempo_de_viagem(50.0), 2.5));
    assert(perto(b.tempo_de_viagem(0.0), 0.0));

    Carro c(100.0, 30);
    // 250 km: 2.5 h de estrada + 2 paradas de 30 min = 3.5 h
    assert(perto(c.tempo_de_viagem(250.0), 3.5));

    // menos de 100 km: nenhuma parada
    assert(perto(c.tempo_de_viagem(80.0), 0.8));

    // exatamente 100 km: uma parada completa
    assert(perto(c.tempo_de_viagem(100.0), 1.5));

    // carro sem paradas se comporta como a bicicleta, so que mais rapido
    Carro sem_parada(50.0, 0);
    assert(perto(sem_parada.tempo_de_viagem(200.0), 4.0));

    // a mesma pergunta, feita pela referencia a classe base
    MeioDeTransporte& transporte = c;
    assert(perto(transporte.tempo_de_viagem(250.0), 3.5));

    MeioDeTransporte& outro = b;
    assert(perto(outro.tempo_de_viagem(50.0), 2.5));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex03

Complete a classe Duracao, que representa um intervalo de tempo em minutos e segundos (campos minutos_ e segundos_, construtor e getters já dados). Implemente operator+ e operator==.

O operator+ não é apenas somar campo a campo. Somar 1min30s com 2min45s campo a campo daria 3min75s, que não é um jeito válido de escrever uma duração: 75 segundos são 1 minuto e 15 segundos. Depois de somar, normalize o resultado — cada 60 segundos viram 1 minuto a mais, e no fim os segundos ficam entre 0 e 59.

É esse o motivo de sobrecarregar o operador em vez de deixar quem chama somar os campos na mão: a regra de normalização mora dentro da classe, e ninguém consegue construir uma Duracao com 75 segundos somando duas válidas.

O operator== compara as duas durações. Como toda Duracao produzida pelo operator+ já está normalizada, basta comparar os dois campos.

Assuma que quem constrói uma Duracao passa segundos entre 0 e 59.

Duracao a(1, 30);      // 1 min 30 s
Duracao b(2, 45);      // 2 min 45 s
Duracao c = a + b;
c.minutos();    // -> 4    (e nao 3)
c.segundos();   // -> 15   (e nao 75)

a == b;                // -> false
Duracao d(1, 30);
a == d;                 // -> true
// ex03 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX03_H
#define EX03_H

class Duracao {
private:
    int minutos_;
    int segundos_;

public:
    Duracao(int minutos, int segundos) : minutos_(minutos), segundos_(segundos) {}

    int minutos() const { return minutos_; }
    int segundos() const { return segundos_; }

    Duracao operator+(const Duracao& outra) const;
    bool operator==(const Duracao& outra) const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
Duracao Duracao::operator+(const Duracao& outra) const {
    // TODO: some os minutos e os segundos separadamente e depois NORMALIZE:
    // cada 60 segundos viram 1 minuto (use / 60 e % 60).
    // Devolva um novo Duracao com o resultado ja normalizado.
    return Duracao(0, 0);
}

bool Duracao::operator==(const Duracao& outra) const {
    // TODO: retorne true se minutos_ e segundos_ forem iguais aos da outra.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Duracao a(1, 30);
    Duracao b(2, 45);

    // 75 segundos precisam virar 1 min e 15 s
    Duracao c = a + b;
    assert(c.minutos() == 4);
    assert(c.segundos() == 15);

    // soma que nao precisa de normalizacao
    Duracao simples = Duracao(1, 10) + Duracao(2, 20);
    assert(simples.minutos() == 3);
    assert(simples.segundos() == 30);

    // exatamente 60 segundos: vira 1 minuto e 0 segundo
    Duracao cheio = Duracao(0, 30) + Duracao(0, 30);
    assert(cheio.minutos() == 1);
    assert(cheio.segundos() == 0);

    // somar zero nao muda nada
    Duracao zero(0, 0);
    Duracao igual = a + zero;
    assert(igual.minutos() == 1);
    assert(igual.segundos() == 30);

    assert(!(a == b));
    Duracao d(1, 30);
    assert(a == d);

    // a soma normalizada e igual a duracao escrita direto
    assert((Duracao(0, 45) + Duracao(0, 45)) == Duracao(1, 30));

    // minutos iguais, segundos diferentes: nao sao iguais
    assert(!(Duracao(2, 0) == Duracao(2, 1)));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex04

Dadas as classes Forma (abstrata), Circulo e Quadrado (todas já implementadas), escreva soma_areas, que recebe um std::vector<std::unique_ptr<Forma>> e devolve a soma das áreas de todas as formas, chamando area() polimorficamente em cada uma.

std::vector<std::unique_ptr<Forma>> formas;
formas.push_back(std::make_unique<Quadrado>(2.0));   // area 4.0
formas.push_back(std::make_unique<Circulo>(1.0));     // area ~3.14159
soma_areas(formas);   // -> ~7.14159
// ex04 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>
#include <vector>
#include <memory>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX04_H
#define EX04_H

class Forma {
public:
    virtual double area() const = 0;
    virtual ~Forma() {}
};

class Circulo : public Forma {
private:
    double raio_;

public:
    explicit Circulo(double raio) : raio_(raio) {}
    double area() const override { return 3.14159 * raio_ * raio_; }
};

class Quadrado : public Forma {
private:
    double lado_;

public:
    explicit Quadrado(double lado) : lado_(lado) {}
    double area() const override { return lado_ * lado_; }
};

// Soma as areas de todas as formas do vector (chamada polimorfica).
double soma_areas(const std::vector<std::unique_ptr<Forma>>& formas);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double soma_areas(const std::vector<std::unique_ptr<Forma>>& formas) {
    // TODO: percorra "formas" (const auto& forma : formas) somando forma->area().
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    std::vector<std::unique_ptr<Forma>> formas;
    formas.push_back(std::make_unique<Quadrado>(2.0));
    formas.push_back(std::make_unique<Circulo>(1.0));
    assert(std::abs((soma_areas(formas)) - (7.14159)) <= (0.001));

    std::vector<std::unique_ptr<Forma>> vazio;
    assert(std::abs((soma_areas(vazio)) - (0.0)) <= (0.0001));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex05

Complete os destrutores de Conexao e ConexaoSegura (ConexaoSegura herda de Conexao, acrescentando a camada de criptografia). Cada destrutor deve fazer duas coisas:

  1. incrementar o seu próprio contador estático (Conexao::destruicoes e ConexaoSegura::destruicoes);
  2. acrescentar a sua letra ao registro estático Conexao::ordem — "C" no destrutor de Conexao e "S" no de ConexaoSegura.

O contador prova que os dois destrutores rodaram. O registro ordem prova em que sequência eles rodaram, e é aí que está a lição: ao destruir um ConexaoSegura, o C++ chama primeiro o destrutor da derivada e depois o da base, de dentro para fora. Faz sentido — a camada de criptografia precisa ser desmontada antes de a conexão de baixo ser fechada, e não depois. O resultado esperado é "SC", nunca "CS".

O destrutor de Conexao já é virtual — isso é proposital, e é o que garante que delete num ponteiro Conexao* apontando para um ConexaoSegura chame os dois destrutores. Sem o virtual, só o de Conexao rodaria: o registro sairia "C" e a camada de cima vazaria.

Conexao::destruicoes = 0;
ConexaoSegura::destruicoes = 0;
Conexao::ordem = "";

Conexao* c = new ConexaoSegura();
delete c;   // ~ConexaoSegura() PRIMEIRO, depois ~Conexao()

Conexao::destruicoes;        // -> 1
ConexaoSegura::destruicoes;  // -> 1
Conexao::ordem;              // -> "SC"
// ex05 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX05_H
#define EX05_H

class Conexao {
public:
    static inline int destruicoes = 0;

    // Registro da ordem em que os destrutores rodaram:
    // "C" para ~Conexao(), "S" para ~ConexaoSegura().
    static inline std::string ordem = "";

    virtual ~Conexao();
};

class ConexaoSegura : public Conexao {
public:
    static inline int destruicoes = 0;

    ~ConexaoSegura() override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
Conexao::~Conexao() {
    // TODO: incremente Conexao::destruicoes e acrescente "C" a Conexao::ordem.
}

ConexaoSegura::~ConexaoSegura() {
    // TODO: incremente ConexaoSegura::destruicoes e acrescente "S" a Conexao::ordem.
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    // destruindo pelo ponteiro para a base: o ~Conexao() virtual e o que
    // faz o destrutor da derivada ser chamado tambem
    Conexao::destruicoes = 0;
    ConexaoSegura::destruicoes = 0;
    Conexao::ordem = "";

    Conexao* c = new ConexaoSegura();
    delete c;

    assert(Conexao::destruicoes == 1);
    assert(ConexaoSegura::destruicoes == 1);
    assert(Conexao::ordem == "SC");   // de dentro para fora, nunca "CS"

    // objeto na pilha: mesma ordem
    Conexao::destruicoes = 0;
    ConexaoSegura::destruicoes = 0;
    Conexao::ordem = "";
    {
        ConexaoSegura s;
    }
    assert(Conexao::destruicoes == 1);
    assert(ConexaoSegura::destruicoes == 1);
    assert(Conexao::ordem == "SC");

    // uma Conexao pura destroi so a si mesma
    Conexao::destruicoes = 0;
    ConexaoSegura::destruicoes = 0;
    Conexao::ordem = "";
    {
        Conexao simples;
    }
    assert(Conexao::destruicoes == 1);
    assert(ConexaoSegura::destruicoes == 0);
    assert(Conexao::ordem == "C");

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex06

Dada a classe base Dispositivo (campos protegidos modelo_ e armazenamento_gb_, construtor e getters já implementados, e detalhes() virtual já implementado na base), complete as duas classes derivadas:

  • Notebook — tem também um sistema_operacional_. Seu detalhes() deve devolver o texto da base seguido de " | SO: <sistema operacional>".
  • Tablet — tem também um bool tem_5g_. Seu detalhes() deve devolver o texto da base seguido de " | 5G: Sim" ou " | 5G: Nao".

Repare que a versão da base continua útil: em vez de repetir a montagem do texto, chame Dispositivo::detalhes() e concatene o resto.

Notebook n("Dell XPS 13", 512, "Linux");
n.detalhes();   // -> "Dell XPS 13 | 512 GB | SO: Linux"

Tablet t("iPad", 64, false);
t.detalhes();   // -> "iPad | 64 GB | 5G: Nao"
// ex06 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <memory>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX06_H
#define EX06_H

class Dispositivo {
protected:
    std::string modelo_;
    int armazenamento_gb_;

public:
    Dispositivo(const std::string& modelo, int armazenamento_gb)
        : modelo_(modelo), armazenamento_gb_(armazenamento_gb) {}
    virtual ~Dispositivo() = default;

    std::string modelo() const { return modelo_; }
    int armazenamento_gb() const { return armazenamento_gb_; }

    virtual std::string detalhes() const {
        return modelo_ + " | " + std::to_string(armazenamento_gb_) + " GB";
    }
};

class Notebook : public Dispositivo {
private:
    std::string sistema_operacional_;

public:
    Notebook(const std::string& modelo, int armazenamento_gb,
             const std::string& sistema_operacional)
        : Dispositivo(modelo, armazenamento_gb),
          sistema_operacional_(sistema_operacional) {}

    std::string detalhes() const override;
};

class Tablet : public Dispositivo {
private:
    bool tem_5g_;

public:
    Tablet(const std::string& modelo, int armazenamento_gb, bool tem_5g)
        : Dispositivo(modelo, armazenamento_gb), tem_5g_(tem_5g) {}

    std::string detalhes() const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string Notebook::detalhes() const {
    // TODO: devolva Dispositivo::detalhes() + " | SO: " + sistema_operacional_.
    return "";
}

std::string Tablet::detalhes() const {
    // TODO: devolva Dispositivo::detalhes() + " | 5G: Sim" ou " | 5G: Nao",
    // conforme o valor de tem_5g_.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Notebook n("Dell XPS 13", 512, "Linux");
    assert(n.detalhes() == std::string("Dell XPS 13 | 512 GB | SO: Linux"));

    Tablet com_5g("Galaxy Tab", 128, true);
    assert(com_5g.detalhes() == std::string("Galaxy Tab | 128 GB | 5G: Sim"));

    Tablet sem_5g("iPad", 64, false);
    assert(sem_5g.detalhes() == std::string("iPad | 64 GB | 5G: Nao"));

    // A base continua funcionando como base.
    Dispositivo generico("Generico", 32);
    assert(generico.detalhes() == std::string("Generico | 32 GB"));

    // Chamado por ponteiro da base, cada objeto usa a sua propria versao.
    std::vector<std::unique_ptr<Dispositivo>> aparelhos;
    aparelhos.push_back(std::make_unique<Notebook>("Dell XPS 13", 512, "Linux"));
    aparelhos.push_back(std::make_unique<Tablet>("iPad", 64, false));
    assert(aparelhos[0]->detalhes() == std::string("Dell XPS 13 | 512 GB | SO: Linux"));
    assert(aparelhos[1]->detalhes() == std::string("iPad | 64 GB | 5G: Nao"));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex07

Dada a classe abstrata Produto (campos protegidos nome_ e preco_, construtor e getters já implementados, método virtual puro calcula_desconto() const), complete as duas classes derivadas:

  • ProdutoEletronico — desconto de 10% do preço se o preço for maior que 1000.0; 5% caso contrário.
  • ProdutoAlimenticio — tem também dias_para_expirar_. Desconto de 20% do preço se faltarem 5 dias ou menos para expirar; 10% caso contrário.

Note que as duas devolvem o valor do desconto, não o preço com desconto.

ProdutoEletronico tv("TV", 2000.0);
tv.calcula_desconto();   // -> 200.0

ProdutoAlimenticio leite("Leite", 10.0, 3);
leite.calcula_desconto();  // -> 2.0
// ex07 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>
#include <string>
#include <vector>
#include <memory>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX07_H
#define EX07_H

class Produto {
protected:
    std::string nome_;
    double preco_;

public:
    Produto(const std::string& nome, double preco) : nome_(nome), preco_(preco) {}
    virtual ~Produto() = default;

    std::string nome() const { return nome_; }
    double preco() const { return preco_; }

    // Retorna o VALOR do desconto (nao o preco ja descontado).
    virtual double calcula_desconto() const = 0;
};

class ProdutoEletronico : public Produto {
public:
    ProdutoEletronico(const std::string& nome, double preco) : Produto(nome, preco) {}

    double calcula_desconto() const override;
};

class ProdutoAlimenticio : public Produto {
private:
    int dias_para_expirar_;

public:
    ProdutoAlimenticio(const std::string& nome, double preco, int dias_para_expirar)
        : Produto(nome, preco), dias_para_expirar_(dias_para_expirar) {}

    double calcula_desconto() const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double ProdutoEletronico::calcula_desconto() const {
    // TODO: 10% de preco_ se preco_ > 1000.0; 5% caso contrario.
    return 0.0;
}

double ProdutoAlimenticio::calcula_desconto() const {
    // TODO: 20% de preco_ se dias_para_expirar_ <= 5; 10% caso contrario.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    ProdutoEletronico tv("TV", 2000.0);
    assert(std::abs((tv.calcula_desconto()) - (200.0)) <= (0.0001));

    ProdutoEletronico fone("Fone", 200.0);
    assert(std::abs((fone.calcula_desconto()) - (10.0)) <= (0.0001));

    // Exatamente 1000 nao passa do limite: fica nos 5%.
    ProdutoEletronico limite("Monitor", 1000.0);
    assert(std::abs((limite.calcula_desconto()) - (50.0)) <= (0.0001));

    ProdutoAlimenticio leite("Leite", 10.0, 3);
    assert(std::abs((leite.calcula_desconto()) - (2.0)) <= (0.0001));

    // Exatamente 5 dias ainda conta como "perto de expirar".
    ProdutoAlimenticio queijo("Queijo", 50.0, 5);
    assert(std::abs((queijo.calcula_desconto()) - (10.0)) <= (0.0001));

    ProdutoAlimenticio arroz("Arroz", 20.0, 90);
    assert(std::abs((arroz.calcula_desconto()) - (2.0)) <= (0.0001));

    // Todos somados por ponteiro da base.
    std::vector<std::unique_ptr<Produto>> carrinho;
    carrinho.push_back(std::make_unique<ProdutoEletronico>("TV", 2000.0));
    carrinho.push_back(std::make_unique<ProdutoAlimenticio>("Leite", 10.0, 3));
    double total = 0.0;
    for (const auto& p : carrinho) {
        total += p->calcula_desconto();
    }
    assert(std::abs((total) - (202.0)) <= (0.0001));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex08

Sistema de triagem de chamados de suporte técnico. Dada a classe abstrata Chamado (campos protegidos id_ e descricao_, construtor e getters já implementados, método virtual puro processar() const), complete as duas classes derivadas:

  • ChamadoHardware — se a descrição contiver as duas palavras "impressora" e "papel", devolve "Verificar papel preso na impressora". Caso contrário, devolve "Encaminhar para suporte de hardware".
  • ChamadoSoftware — se a descrição contiver as duas palavras "erro" e "instalacao", devolve "Realizar reinstalacao do software". Caso contrário, devolve "Encaminhar para suporte de software".

Use descricao_.find("palavra") != std::string::npos para testar se a palavra aparece. Considere que as descrições vêm em minúsculas e sem acento.

ChamadoHardware c(1, "a impressora esta com papel preso");
c.processar();   // -> "Verificar papel preso na impressora"
// ex08 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <memory>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX08_H
#define EX08_H

class Chamado {
protected:
    int id_;
    std::string descricao_;

public:
    Chamado(int id, const std::string& descricao) : id_(id), descricao_(descricao) {}
    virtual ~Chamado() = default;

    int id() const { return id_; }
    std::string descricao() const { return descricao_; }

    // Retorna a orientacao de atendimento para este chamado.
    virtual std::string processar() const = 0;
};

class ChamadoHardware : public Chamado {
public:
    ChamadoHardware(int id, const std::string& descricao) : Chamado(id, descricao) {}

    std::string processar() const override;
};

class ChamadoSoftware : public Chamado {
public:
    ChamadoSoftware(int id, const std::string& descricao) : Chamado(id, descricao) {}

    std::string processar() const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string ChamadoHardware::processar() const {
    // TODO: se descricao_ contiver "impressora" E "papel", devolva
    // "Verificar papel preso na impressora".
    // Caso contrario, "Encaminhar para suporte de hardware".
    return "";
}

std::string ChamadoSoftware::processar() const {
    // TODO: se descricao_ contiver "erro" E "instalacao", devolva
    // "Realizar reinstalacao do software".
    // Caso contrario, "Encaminhar para suporte de software".
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    ChamadoHardware preso(1, "a impressora esta com papel preso");
    assert(preso.processar() == std::string("Verificar papel preso na impressora"));

    // So uma das duas palavras nao basta.
    ChamadoHardware so_impressora(2, "a impressora nao liga");
    assert(so_impressora.processar() == std::string("Encaminhar para suporte de hardware"));

    ChamadoHardware outro(3, "monitor com listras");
    assert(outro.processar() == std::string("Encaminhar para suporte de hardware"));

    ChamadoSoftware reinstalar(4, "erro durante a instalacao do sistema");
    assert(reinstalar.processar() == std::string("Realizar reinstalacao do software"));

    ChamadoSoftware so_erro(5, "erro ao abrir o programa");
    assert(so_erro.processar() == std::string("Encaminhar para suporte de software"));

    // A fila de triagem trata todos pela classe base.
    std::vector<std::unique_ptr<Chamado>> fila;
    fila.push_back(std::make_unique<ChamadoHardware>(6, "papel preso na impressora"));
    fila.push_back(std::make_unique<ChamadoSoftware>(7, "nao consigo entrar"));
    assert(fila[0]->processar() == std::string("Verificar papel preso na impressora"));
    assert(fila[1]->processar() == std::string("Encaminhar para suporte de software"));
    assert(fila[0]->id() == 6);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex09

Dadas as classes Forma (abstrata), Circulo e Quadrado — todas já implementadas —, escreva indice_maior_area, que recebe um std::vector<std::unique_ptr<Forma>> e devolve o índice da forma de maior área, ou -1 se o vector estiver vazio. Em caso de empate, devolva o índice da primeira.

É a busca do melhor até agora da Lista 05, com uma diferença: o valor comparado não está guardado em lugar nenhum — ele vem de formas[i]->area(), uma chamada polimórfica que só se resolve em tempo de execução. A função não sabe, e não precisa saber, quais tipos de forma existem.

Vale o mesmo cuidado de sempre: comece pelo índice 0, e não por uma área inicial 0.

// {Quadrado(2), Circulo(3), Quadrado(1)}
indice_maior_area(formas)   // -> 1
// ex09 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <vector>
#include <memory>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX09_H
#define EX09_H

class Forma {
public:
    virtual double area() const = 0;
    virtual ~Forma() = default;
};

class Circulo : public Forma {
private:
    double raio_;
public:
    explicit Circulo(double raio) : raio_(raio) {}
    double area() const override { return 3.14159 * raio_ * raio_; }
};

class Quadrado : public Forma {
private:
    double lado_;
public:
    explicit Quadrado(double lado) : lado_(lado) {}
    double area() const override { return lado_ * lado_; }
};

// Retorna o indice da forma de MAIOR area, ou -1 se o vector
// estiver vazio. Em caso de empate, devolve o indice da primeira.
int indice_maior_area(const std::vector<std::unique_ptr<Forma>>& formas);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int indice_maior_area(const std::vector<std::unique_ptr<Forma>>& formas) {
    // TODO: e a busca do melhor ate agora da Lista 05, so que o valor comparado
    // vem de uma chamada polimorfica: formas[i]->area().
    // Comece pelo indice 0 (nao por area 0) e trate o vector vazio.
    return -1;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    std::vector<std::unique_ptr<Forma>> formas;
    formas.push_back(std::make_unique<Quadrado>(2.0));   // area 4
    formas.push_back(std::make_unique<Circulo>(3.0));    // area ~28.3
    formas.push_back(std::make_unique<Quadrado>(1.0));   // area 1
    assert(indice_maior_area(formas) == 1);

    std::vector<std::unique_ptr<Forma>> uma;
    uma.push_back(std::make_unique<Quadrado>(5.0));
    assert(indice_maior_area(uma) == 0);

    // empate: fica a primeira
    std::vector<std::unique_ptr<Forma>> empate;
    empate.push_back(std::make_unique<Quadrado>(2.0));
    empate.push_back(std::make_unique<Quadrado>(2.0));
    assert(indice_maior_area(empate) == 0);

    std::vector<std::unique_ptr<Forma>> vazio;
    assert(indice_maior_area(vazio) == -1);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex10

Dada a classe abstrata Funcionario (método virtual puro salario() const e destrutor virtual, já declarados), complete Vendedor (salário fixo de 1500.0 mais 100.0 por venda registrada no construtor) e Gerente (salário fixo de 4000.0, sem variação).

O último caso de teste é o que importa: a chamada através de Funcionario& precisa executar a versão da classe concreta. É para isso que o salario() da base é virtual.

Vendedor v(3);        // 3 vendas
v.salario();            // -> 1500.0 + 3*100.0 = 1800.0

Gerente g;
g.salario();             // -> 4000.0

Funcionario& f = v;
f.salario();               // -> 1800.0 (chamada polimorfica)
// ex10 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX10_H
#define EX10_H

class Funcionario {
public:
    virtual double salario() const = 0;
    virtual ~Funcionario() {}
};

class Vendedor : public Funcionario {
private:
    int vendas_;

public:
    explicit Vendedor(int vendas) : vendas_(vendas) {}
    double salario() const override;
};

class Gerente : public Funcionario {
public:
    double salario() const override;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double Vendedor::salario() const {
    // TODO: retorne 1500.0 + 100.0 * vendas_.
    return 0.0;
}

double Gerente::salario() const {
    // TODO: retorne 4000.0.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Vendedor v(3);
    assert(std::abs((v.salario()) - (1800.0)) <= (0.0001));

    Gerente g;
    assert(std::abs((g.salario()) - (4000.0)) <= (0.0001));

    Funcionario& f = v;
    assert(std::abs((f.salario()) - (1800.0)) <= (0.0001));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}