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 vezesavanca()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 moedas0.25,0.50e1.00. Se o valor for uma dessas, some ao saldo e devolvatrue; qualquer outro valor é rejeitado sem alterar o saldo, devolvendofalse.compra(double preco, int quantidade)— se o saldo cobrirpreco * quantidade, desconte o total e devolvatrue. Se não cobrir, não altere o saldo e devolvafalse.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.
// 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;
}