Pular para conteúdo

Lista 09 · Classes, encapsulamento e construtores

Exercícios da Semana 10 — Classes, encapsulamento e construtores. Em todos os exercícios, os campos são privados — o objetivo é garantir o invariante da classe através dos métodos públicos, nunca deixando o objeto chegar a um estado inválido.

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

Complete a classe Contador (campo privado valor_, já declarado, inicializado em 0 pelo construtor já dado). Implemente incrementa() (soma 1), decrementa() (subtrai 1, mas nunca deixa valor_ ficar negativo — se já está em 0, decrementa() não faz nada) e valor() const.

Contador c;
c.incrementa();
c.incrementa();
c.decrementa();
c.valor();       // -> 1

Contador c2;
c2.decrementa();  // ja esta em 0 -- nao faz nada
c2.valor();        // -> 0
// 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>

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

class Contador {
private:
    int valor_;

public:
    Contador() : valor_(0) {}

    void incrementa();
    void decrementa();
    int valor() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Contador::incrementa() {
    // TODO: some 1 a valor_.
}

void Contador::decrementa() {
    // TODO: subtraia 1 de valor_, mas so se valor_ for maior que 0.
}

int Contador::valor() const {
    // TODO: retorne valor_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Contador c;
    c.incrementa();
    c.incrementa();
    c.decrementa();
    assert(c.valor() == 1);

    Contador c2;
    c2.decrementa();
    assert(c2.valor() == 0);

    Contador c3;
    c3.incrementa();
    c3.incrementa();
    c3.incrementa();
    assert(c3.valor() == 3);

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

ex02

Complete a classe Semaforo, que guarda a cor atual num enum class Cor { Verde, Amarelo, Vermelho } (já declarado no .h). Implemente:

  • avanca() — passa para a próxima cor do ciclo: Verde → Amarelo → Vermelho → Verde, e assim por diante, para sempre;
  • cor() const — devolve a cor atual;
  • trocas() const — quantas vezes avanca() já foi chamado.

O construtor já deixa o semáforo em Cor::Vermelho com zero trocas.

O ponto do exercício é a invariante: um semáforo só pode estar numa das três cores, e só pode sair de uma cor para a próxima do ciclo. Não existe define_cor() — se existisse, qualquer um poderia pular do verde direto para o vermelho, que é justamente o que não pode acontecer no mundo real. Com o campo privado e só o avanca() público, essa regra é impossível de violar de fora da classe.

Escreva o avanca() com um switch sobre a cor atual, ou com três if. Repare que Cor::Vermelho volta para Cor::Verde — é o único ponto do ciclo em que a "próxima" cor não vem depois na declaração do enum, e é onde a turma costuma esquecer o caso.

Semaforo s;
s.cor();       // -> Cor::Vermelho
s.avanca();
s.cor();       // -> Cor::Verde
s.avanca();
s.cor();       // -> Cor::Amarelo
s.avanca();
s.cor();       // -> Cor::Vermelho   (deu a volta)
s.trocas();    // -> 3
// 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>

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

enum class Cor { Verde, Amarelo, Vermelho };

class Semaforo {
private:
    Cor cor_;
    int trocas_;

public:
    Semaforo() : cor_(Cor::Vermelho), trocas_(0) {}

    void avanca();
    Cor cor() const;
    int trocas() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Semaforo::avanca() {
    // TODO: passe para a proxima cor do ciclo
    // (Verde -> Amarelo -> Vermelho -> Verde) e incremente trocas_.
    // Nao esqueca do caso Vermelho, que volta para Verde.
}

Cor Semaforo::cor() const {
    // TODO: retorne cor_.
    return Cor::Vermelho;
}

int Semaforo::trocas() const {
    // TODO: retorne trocas_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Semaforo s;
    assert(s.cor() == Cor::Vermelho);
    assert(s.trocas() == 0);

    s.avanca();
    assert(s.cor() == Cor::Verde);
    assert(s.trocas() == 1);

    s.avanca();
    assert(s.cor() == Cor::Amarelo);

    s.avanca();
    assert(s.cor() == Cor::Vermelho);   // deu a volta no ciclo
    assert(s.trocas() == 3);

    // duas voltas completas: o ciclo nao pode "parar" no vermelho
    for (int i = 0; i < 6; i++) {
        s.avanca();
    }
    assert(s.cor() == Cor::Vermelho);
    assert(s.trocas() == 9);

    // cada semaforo tem o proprio estado
    Semaforo outro;
    assert(outro.cor() == Cor::Vermelho);
    assert(outro.trocas() == 0);

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

ex03

Complete a classe Estoque (campo privado quantidade_, inicializado em 0 pelo construtor já dado). Implemente adiciona(int quantidade) (soma, ignorando valores negativos), remove(int quantidade) — devolve true e subtrai se houver saldo suficiente, false e não altera nada caso contrário — e quantidade() const.

Estoque e;
e.adiciona(10);
bool ok = e.remove(3);
e.quantidade();   // -> 7
ok;                // -> true

bool falhou = e.remove(100);
e.quantidade();   // -> 7 (nao mudou)
falhou;             // -> false
// 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 Estoque {
private:
    int quantidade_;

public:
    Estoque() : quantidade_(0) {}

    void adiciona(int quantidade);
    bool remove(int quantidade);
    int quantidade() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Estoque::adiciona(int quantidade) {
    // TODO: se quantidade > 0, some a quantidade_.
}

bool Estoque::remove(int quantidade) {
    // TODO: se quantidade > 0 e quantidade <= quantidade_, subtraia e retorne true.
    // Caso contrario, nao altere nada e retorne false.
    return false;
}

int Estoque::quantidade() const {
    // TODO: retorne quantidade_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Estoque e;
    e.adiciona(10);
    bool ok = e.remove(3);
    assert(e.quantidade() == 7);
    assert(ok);

    bool falhou = e.remove(100);
    assert(e.quantidade() == 7);
    assert(!(falhou));

    e.adiciona(-5);
    assert(e.quantidade() == 7);

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

ex04

Complete a classe RetanguloValidado: o construtor recebe largura e altura, mas valores negativos devem virar 0 (não é possível ter um retângulo com lado negativo). Implemente area() const, perimetro() const e eh_quadrado() const (true se largura == altura).

RetanguloValidado r1(4.0, 3.0);
r1.area();         // -> 12.0
r1.eh_quadrado();  // -> false

RetanguloValidado r2(-5.0, 5.0);  // largura negativa vira 0
r2.area();          // -> 0.0

RetanguloValidado r3(5.0, 5.0);
r3.eh_quadrado();  // -> true
// 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>

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

class RetanguloValidado {
private:
    double largura_;
    double altura_;

public:
    RetanguloValidado(double largura, double altura);

    double area() const;
    double perimetro() const;
    bool eh_quadrado() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
RetanguloValidado::RetanguloValidado(double largura, double altura)
    // TODO: use a lista de inicializacao para definir largura_ e altura_,
    // trocando valores negativos por 0 (ex: largura >= 0 ? largura : 0.0).
    : largura_(0.0), altura_(0.0) {
}

double RetanguloValidado::area() const {
    // TODO: retorne largura_ * altura_.
    return 0.0;
}

double RetanguloValidado::perimetro() const {
    // TODO: retorne 2 * (largura_ + altura_).
    return 0.0;
}

bool RetanguloValidado::eh_quadrado() const {
    // TODO: retorne true se largura_ == altura_.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    RetanguloValidado r1(4.0, 3.0);
    assert(std::abs((r1.area()) - (12.0)) <= (0.0001));
    assert(!(r1.eh_quadrado()));

    RetanguloValidado r2(-5.0, 5.0);
    assert(std::abs((r2.area()) - (0.0)) <= (0.0001));

    RetanguloValidado r3(5.0, 5.0);
    assert(r3.eh_quadrado());
    assert(std::abs((r3.perimetro()) - (20.0)) <= (0.0001));

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

ex05

Complete a classe Pessoa (construtor recebe nome e idade iniciais, já dado). Implemente os getters nome() const e idade() const, e o setter define_idade(int nova_idade), que só atualiza idade_ se nova_idade >= 0 (idades negativas são ignoradas, sem erro).

Pessoa p("Ana", 25);
p.define_idade(26);
p.idade();   // -> 26

p.define_idade(-10);  // invalido -- ignorado
p.idade();              // -> 26 (nao mudou)
// 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 Pessoa {
private:
    std::string nome_;
    int idade_;

public:
    Pessoa(std::string nome, int idade) : nome_(nome), idade_(idade) {}

    std::string nome() const;
    int idade() const;
    void define_idade(int nova_idade);
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string Pessoa::nome() const {
    // TODO: retorne nome_.
    return "";
}

int Pessoa::idade() const {
    // TODO: retorne idade_.
    return 0;
}

void Pessoa::define_idade(int nova_idade) {
    // TODO: so atualize idade_ se nova_idade >= 0.
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Pessoa p("Ana", 25);
    assert(p.nome() == std::string("Ana"));
    assert(p.idade() == 25);

    p.define_idade(26);
    assert(p.idade() == 26);

    p.define_idade(-10);
    assert(p.idade() == 26);

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

ex06

Complete a classe BoletimAluno (campo privado notas_ do tipo std::vector<double>, já declarado, começa vazio). Implemente adiciona_nota(double nota) (adiciona ao final), media() const (média das notas; retorne 0.0 se não houver nenhuma) e aprovado() const (true se media() >= 6.0).

BoletimAluno b;
b.adiciona_nota(8.0);
b.adiciona_nota(6.0);
b.media();       // -> 7.0
b.aprovado();   // -> true
// 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 <cmath>
#include <vector>

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

class BoletimAluno {
private:
    std::vector<double> notas_;

public:
    void adiciona_nota(double nota);
    double media() const;
    bool aprovado() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void BoletimAluno::adiciona_nota(double nota) {
    // TODO: adicione "nota" ao final de notas_ (push_back).
}

double BoletimAluno::media() const {
    // TODO: se notas_ estiver vazio, retorne 0.0. Senao, retorne a media.
    return 0.0;
}

bool BoletimAluno::aprovado() const {
    // TODO: retorne true se media() >= 6.0 (voce pode chamar media() aqui dentro).
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    BoletimAluno b;
    b.adiciona_nota(8.0);
    b.adiciona_nota(6.0);
    assert(std::abs((b.media()) - (7.0)) <= (0.0001));
    assert(b.aprovado());

    BoletimAluno vazio;
    assert(std::abs((vazio.media()) - (0.0)) <= (0.0001));

    BoletimAluno reprovado;
    reprovado.adiciona_nota(3.0);
    reprovado.adiciona_nota(4.0);
    assert(!(reprovado.aprovado()));

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

ex07

Esta é a atividade da máquina de vendas, agora como uma classe. A versão antiga era um programa de menu com uma variável global saldo; aqui o saldo é um campo privado, e é a classe que garante que ninguém mexa nele por fora.

Complete a classe MaquinaDeVendas (campo privado saldo_, já declarado, começa em 0.0):

  • insere_moeda(double valor) — aceita apenas as moedas 0.25, 0.50 e 1.00. Se o valor for uma dessas, some ao saldo e devolva true; qualquer outro valor é rejeitado sem alterar o saldo, devolvendo false.
  • compra(double preco, int quantidade) — se o saldo cobrir preco * quantidade, desconte o total e devolva true. Se não cobrir, não altere o saldo e devolva false.
  • saldo() const — devolve o saldo atual.
  • saca() — devolve o saldo atual e zera o saldo da máquina.
MaquinaDeVendas m;
m.insere_moeda(0.25);   // -> true,  saldo 0.25
m.insere_moeda(0.30);   // -> false, saldo continua 0.25
m.insere_moeda(1.00);   // -> true,  saldo 1.25
m.compra(0.50, 2);      // -> true,  saldo 0.25   (agua, 2 unidades)
m.compra(1.50, 1);      // -> false, saldo continua 0.25
m.saca();               // -> 0.25,  saldo volta a 0.00
// 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>

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

class MaquinaDeVendas {
private:
    double saldo_ = 0.0;

public:
    // Aceita apenas 0.25, 0.50 e 1.00. Retorna true se a moeda foi aceita.
    bool insere_moeda(double valor);

    // Desconta preco * quantidade se o saldo cobrir. Retorna true se comprou.
    bool compra(double preco, int quantidade);

    double saldo() const;

    // Retorna o saldo atual e zera o saldo da maquina.
    double saca();
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool MaquinaDeVendas::insere_moeda(double valor) {
    // TODO: se valor for 0.25, 0.50 ou 1.00, some ao saldo_ e devolva true.
    // Qualquer outro valor: nao mexa no saldo_ e devolva false.
    return false;
}

bool MaquinaDeVendas::compra(double preco, int quantidade) {
    // TODO: calcule o total (preco * quantidade). Se saldo_ for suficiente,
    // desconte e devolva true; senao devolva false sem alterar saldo_.
    return false;
}

double MaquinaDeVendas::saldo() const {
    // TODO: devolva saldo_.
    return 0.0;
}

double MaquinaDeVendas::saca() {
    // TODO: guarde o saldo_ atual numa variavel, zere saldo_ e devolva o valor guardado.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    MaquinaDeVendas m;
    assert(std::abs((m.saldo()) - (0.0)) <= (0.0001));

    assert(m.insere_moeda(0.25));
    assert(std::abs((m.saldo()) - (0.25)) <= (0.0001));

    // Moeda invalida: rejeitada, e o saldo nao pode mudar.
    assert(!(m.insere_moeda(0.30)));
    assert(std::abs((m.saldo()) - (0.25)) <= (0.0001));

    assert(m.insere_moeda(1.00));
    assert(std::abs((m.saldo()) - (1.25)) <= (0.0001));

    // Duas aguas a 0.50: cabe no saldo.
    assert(m.compra(0.50, 2));
    assert(std::abs((m.saldo()) - (0.25)) <= (0.0001));

    // Um suco a 1.50: nao cabe, e o saldo continua igual.
    assert(!(m.compra(1.50, 1)));
    assert(std::abs((m.saldo()) - (0.25)) <= (0.0001));

    assert(std::abs((m.saca()) - (0.25)) <= (0.0001));
    assert(std::abs((m.saldo()) - (0.0)) <= (0.0001));

    // Sacar de uma maquina vazia devolve 0.
    assert(std::abs((m.saca()) - (0.0)) <= (0.0001));

    // Comprar exatamente o saldo disponivel e permitido.
    MaquinaDeVendas exata;
    assert(exata.insere_moeda(0.50));
    assert(exata.compra(0.50, 1));
    assert(std::abs((exata.saldo()) - (0.0)) <= (0.0001));

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

ex08

Complete a classe Historico (campo privado leituras_, std::vector<double>, já declarado). Implemente registra(double), quantidade() const e, o exercício de verdade, maior_sequencia_crescente() const.

Este último devolve o tamanho da maior sequência de leituras estritamente crescentes consecutivas. Um histórico vazio devolve 0; uma leitura sozinha já é uma sequência de tamanho 1.

A dica é manter duas variáveis: o tamanho da sequência atual e o maior tamanho já visto. Quando a sequência quebra, a atual volta a 1 — mas a maior não se perde.

Historico h;   // registra 1, 2, 3, 1, 2
h.maior_sequencia_crescente();   // -> 3
// 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 <vector>

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

class Historico {
private:
    std::vector<double> leituras_;

public:
    void registra(double leitura);
    int quantidade() const;

    // Retorna o tamanho da maior sequencia de leituras ESTRITAMENTE
    // crescentes consecutivas. Historico vazio devolve 0; uma leitura
    // sozinha ja e uma sequencia de tamanho 1.
    int maior_sequencia_crescente() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Historico::registra(double leitura) {
    // TODO: acrescente a leitura ao fim de leituras_.
}

int Historico::quantidade() const {
    // TODO: devolva o tamanho de leituras_.
    return 0;
}

int Historico::maior_sequencia_crescente() const {
    // TODO: percorra as leituras mantendo DUAS variaveis: o tamanho da
    // sequencia atual e o maior tamanho ja visto. Toda vez que a leitura
    // for maior que a anterior, a atual cresce; senao, ela volta a 1.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Historico vazio;
    assert(vazio.quantidade() == 0);
    assert(vazio.maior_sequencia_crescente() == 0);

    Historico uma;
    uma.registra(5.0);
    assert(uma.maior_sequencia_crescente() == 1);

    Historico h;
    for (double x : {1.0, 2.0, 3.0, 1.0, 2.0}) {
        h.registra(x);
    }
    assert(h.quantidade() == 5);
    assert(h.maior_sequencia_crescente() == 3);

    Historico caindo;
    for (double x : {5.0, 4.0, 3.0}) {
        caindo.registra(x);
    }
    assert(caindo.maior_sequencia_crescente() == 1);

    // valores iguais quebram a sequencia: tem que ser ESTRITAMENTE crescente
    Historico iguais;
    for (double x : {1.0, 1.0, 2.0}) {
        iguais.registra(x);
    }
    assert(iguais.maior_sequencia_crescente() == 2);

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

ex09

Complete a classe Ingresso (campos privados preco_ e usado_, já declarados). O construtor recebe o preço; preços negativos devem virar 0.0. Implemente preco() const, usa() (marca o ingresso como usado — só tem efeito se ainda não tiver sido usado) e esta_usado() const.

Repare no que o construtor faz com o preço negativo: ele não confia no valor que chegou. Esse é o papel do construtor num objeto encapsulado — garantir que não exista Ingresso em estado inválido, nem por um instante.

Ingresso i(50.0);
i.esta_usado();  // -> false
i.usa();
i.esta_usado();  // -> true

Ingresso invalido(-10.0);
invalido.preco();  // -> 0.0
// 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 <cmath>

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

class Ingresso {
private:
    double preco_;
    bool usado_;

public:
    explicit Ingresso(double preco);

    double preco() const;
    void usa();
    bool esta_usado() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
Ingresso::Ingresso(double preco)
    // TODO: use a lista de inicializacao para definir preco_ (trocando
    // negativo por 0.0) e usado_ (comeca false).
    : preco_(0.0), usado_(false) {
}

double Ingresso::preco() const {
    // TODO: retorne preco_.
    return 0.0;
}

void Ingresso::usa() {
    // TODO: marque usado_ como true.
}

bool Ingresso::esta_usado() const {
    // TODO: retorne usado_.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Ingresso i(50.0);
    assert(std::abs((i.preco()) - (50.0)) <= (0.0001));
    assert(!(i.esta_usado()));
    i.usa();
    assert(i.esta_usado());

    Ingresso invalido(-10.0);
    assert(std::abs((invalido.preco()) - (0.0)) <= (0.0001));

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