Tarefa 1: Gerenciamento de Notas com Composição
Código Fonte
CalculoNotas.hpp
#pragma once
#include <vector>
#include <array>
#include <string>
class CalculoNotas {
public:
CalculoNotas(const std::string &nomeDisciplina);
void inserirDados(int qtd); // Inserir qtd notas
void exibirDados() const; // Exibir notas
void ordenar(bool crescente = false); // Ordenar (padrão decrescente)
int obterMinimo() const; // Retornar nota mínima (-1 se vazio)
int obterMaximo() const; // Retornar nota máxima (-1 se vazio)
double calcularMedia() const; // Retornar média (0.0 se vazio)
void mostrarInfo(); // Exibir informações da disciplina
private:
void atualizarEstatisticas(); // Calcular estatísticas
private:
std::string nomeDisciplina; // Nome da disciplina
std::vector<int> notas; // Notas da disciplina
std::array<int, 5> contagens; // Contagem por faixa ([0,60), [60,70), [70,80), [80,90), [90,100])
std::array<double, 5> taxas; // Proporção por faixa
bool dadosAlterados; // Flag para dados alterados
};
CalculoNotas.cpp
#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
#include "CalculoNotas.hpp"
CalculoNotas::CalculoNotas(const std::string &nomeDisciplina): nomeDisciplina{nomeDisciplina}, dadosAlterados{true} {
contagens.fill(0);
taxas.fill(0);
}
void CalculoNotas::inserirDados(int qtd) {
if(qtd < 0) {
std::cerr << "Entrada inválida! Quantidade não pode ser negativa\n";
std::exit(1);
}
notas.reserve(qtd);
int nota;
for(int i = 0; i < qtd;) {
std::cin >> nota;
if(nota < 0 || nota > 100) {
std::cerr << "Entrada inválida! Nota deve estar em [0,100]\n";
continue;
}
notas.push_back(nota);
++i;
}
dadosAlterados = true;
}
void CalculoNotas::exibirDados() const {
for(auto nota: notas)
std::cout << nota << ' ';
std::cout << std::endl;
}
void CalculoNotas::ordenar(bool crescente) {
if(crescente)
std::sort(notas.begin(), notas.end());
else
std::sort(notas.begin(), notas.end(), std::greater<int>());
}
int CalculoNotas::obterMinimo() const {
if(notas.empty())
return -1;
auto it = std::min_element(notas.begin(), notas.end());
return *it;
}
int CalculoNotas::obterMaximo() const {
if(notas.empty())
return -1;
auto it = std::max_element(notas.begin(), notas.end());
return *it;
}
double CalculoNotas::calcularMedia() const {
if(notas.empty())
return 0.0;
double media = std::accumulate(notas.begin(), notas.end(), 0.0)/notas.size();
return media;
}
void CalculoNotas::mostrarInfo() {
if(dadosAlterados)
atualizarEstatisticas();
std::cout << "Disciplina:\t" << nomeDisciplina << std::endl;
std::cout << "Média:\t" << std::fixed << std::setprecision(2) << calcularMedia() << std::endl;
std::cout << "Máximo:\t" << obterMaximo() << std::endl;
std::cout << "Mínimo:\t" << obterMinimo() << std::endl;
std::vector<int> temp = notas;
std::sort(temp.begin(), temp.end());
if (notas.size() % 2 == 1)
std::cout << "Mediana:\t" << static_cast<double>(temp[notas.size() / 2]) << '\n';
else
std::cout << "Mediana:\t" << static_cast<double>((temp[notas.size() / 2 - 1] + temp[notas.size() / 2]) / 2.0) << '\n';
const std::array<std::string, 5> faixas{"[0, 60) ",
"[60, 70)",
"[70, 80)",
"[80, 90)",
"[90, 100]"};
for(int i = static_cast<int>(faixas.size())-1; i >= 0; --i)
std::cout << faixas[i] << "\t: " << contagens[i] << " alunos\t"
<< std::fixed << std::setprecision(2) << taxas[i]*100 << "%\n";
}
void CalculoNotas::atualizarEstatisticas() {
if(notas.empty())
return;
contagens.fill(0);
taxas.fill(0.0);
for(auto nota:notas) {
if(nota < 60)
++contagens[0];
else if (nota < 70)
++contagens[1];
else if (nota < 80)
++contagens[2];
else if (nota < 90)
++contagens[3];
else
++contagens[4];
}
for(size_t i = 0; i < taxas.size(); ++i)
taxas[i] = contagens[i] * 1.0 / notas.size();
dadosAlterados = false;
}
teste1.cpp
#include <iostream>
#include <string>
#include "CalculoNotas.hpp"
void teste() {
CalculoNotas c1("POO");
std::cout << "Inserir notas:\n";
c1.inserirDados(5);
std::cout << "Exibir notas:\n";
c1.exibirDados();
std::cout << "Notas ordenadas:\n";
c1.ordenar(); c1.exibirDados();
std::cout << "*************Estatísticas*************\n";
c1.mostrarInfo();
}
int main() {
teste();
}
Resultados do Teste
Execução demonstra entrada, ordenação e exibição de estatísticas.
Respostas às Questões
- Questão 1: Relação de composição: a classe CalculoNotas contém membros como std::string nomeDisciplina, std::vector<int> notas, etc.
- Questão 2: c.inserirDados(5) é legal, pois é um método público. c.push_back(97) não é legal, pois CalculoNotas não herda ou expõe push_back.
- Questão 3: (1) atualizarEstatisticas() é chamada apenas quando dadosAlterados é true. (2) Não precisa mudar a chamada; basta atualizar a flag.
- Questão 4: A mediana é calculada em mostrarInfo(), usando ordenação temporária.
- Questão 5: As linhas contagens.fill(0) e taxas.fill(0) em atualizarEstatisticas() são necessárias parra resetar os acumuladores.
- Questão 6: (1) Sem impacto funcional. (2) Melhora performance por alocação prévia de memória.
Tarefa 2: Gerenciamento de Notas com Herança
Código Fonte
CalculoNotasHeranca.hpp
#pragma once
#include <array>
#include <string>
#include <vector>
class CalculoNotasHeranca: private std::vector<int> {
public:
CalculoNotasHeranca(const std::string &nomeDisciplina);
void inserirDados(int qtd);
void exibirDados() const;
void ordenar(bool crescente = false);
int obterMinimo() const;
int obterMaximo() const;
double calcularMedia() const;
void mostrarInfo();
private:
void atualizarEstatisticas();
private:
std::string nomeDisciplina;
std::array<int, 5> contagens;
std::array<double, 5> taxas;
bool dadosAlterados;
};
CalculoNotasHeranca.cpp
#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
#include "CalculoNotasHeranca.hpp"
CalculoNotasHeranca::CalculoNotasHeranca(const std::string &nomeDisciplina): nomeDisciplina{nomeDisciplina}, dadosAlterados{true}{
contagens.fill(0);
taxas.fill(0);
}
void CalculoNotasHeranca::inserirDados(int qtd) {
if(qtd < 0) {
std::cerr << "Entrada inválida! Quantidade não pode ser negativa\n";
return;
}
this->reserve(qtd);
int nota;
for(int i = 0; i < qtd;) {
std::cin >> nota;
if(nota < 0 || nota > 100) {
std::cerr << "Entrada inválida! Nota deve estar em [0,100]\n";
continue;
}
this->push_back(nota);
++i;
}
dadosAlterados = true;
}
void CalculoNotasHeranca::exibirDados() const {
for(auto nota: *this)
std::cout << nota << ' ';
std::cout << std::endl;
}
void CalculoNotasHeranca::ordenar(bool crescente) {
if(crescente)
std::sort(this->begin(), this->end());
else
std::sort(this->begin(), this->end(), std::greater<int>());
}
int CalculoNotasHeranca::obterMinimo() const {
if(this->empty())
return -1;
return *std::min_element(this->begin(), this->end());
}
int CalculoNotasHeranca::obterMaximo() const {
if(this->empty())
return -1;
return *std::max_element(this->begin(), this->end());
}
double CalculoNotasHeranca::calcularMedia() const {
if(this->empty())
return 0.0;
double media = std::accumulate(this->begin(), this->end(), 0.0) / this->size();
return media;
}
void CalculoNotasHeranca::mostrarInfo() {
if(dadosAlterados)
atualizarEstatisticas();
std::cout << "Disciplina:\t" << nomeDisciplina << std::endl;
std::cout << "Média:\t" << std::fixed << std::setprecision(2) << calcularMedia() << std::endl;
std::cout << "Máximo:\t" << obterMaximo() << std::endl;
std::cout << "Mínimo:\t" << obterMinimo() << std::endl;
const std::array<std::string, 5> faixas{"[0, 60) ",
"[60, 70)",
"[70, 80)",
"[80, 90)",
"[90, 100]"};
for(int i = static_cast<int>(faixas.size())-1; i >= 0; --i)
std::cout << faixas[i] << "\t: " << contagens[i] << " alunos\t"
<< std::fixed << std::setprecision(2) << taxas[i]*100 << "%\n";
}
void CalculoNotasHeranca::atualizarEstatisticas() {
if(this->empty())
return;
contagens.fill(0);
taxas.fill(0);
for(int nota: *this) {
if(nota < 60)
++contagens[0];
else if (nota < 70)
++contagens[1];
else if (nota < 80)
++contagens[2];
else if (nota < 90)
++contagens[3];
else
++contagens[4];
}
for(size_t i = 0; i < taxas.size(); ++i)
taxas[i] = contagens[i] * 1.0 / this->size();
dadosAlterados = false;
}
teste2.cpp
#include <iostream>
#include <string>
#include "CalculoNotasHeranca.hpp"
void teste() {
CalculoNotasHeranca c1("POO");
std::cout << "Inserir notas:\n";
c1.inserirDados(5);
std::cout << "Exibir notas:\n";
c1.exibirDados();
std::cout << "Notas ordenadas:\n";
c1.ordenar(); c1.exibirDados();
std::cout << "*************Estatísticas*************\n";
c1.mostrarInfo();
}
int main() {
teste();
}
Resultados do Teste
Similar à Tarefa 1, mas com implementação baseada em herança.
Respostas às Questões
- Questão 1: Relação de herança: class CalculoNotasHeranca : private std::vector<int>
- Questão 2: Os métodos de vector não são automaticamente públicos devido à herança privada. push_back não é acessível externamente.
- Questão 3: for(auto nota : notas) usa iteradores do vector; for(int nota : *this) requer begin()/end() na classe.
- Questão 4: Composição é mais flexível; herança fixa a estrutura de armazenamento.
Tarefa 3: Hierarquia de Gráficos com Polimorfismo
Código Fonte
Grafico.hpp
#pragma once
#include <string>
#include <vector>
enum class TipoGrafico {circulo, triangulo, retangulo};
// Classe base
class Grafico {
public:
virtual void desenhar() {}
virtual ~Grafico() = default;
};
// Derivadas
class Circulo : public Grafico {
public:
void desenhar() override;
};
class Triangulo : public Grafico {
public:
void desenhar() override;
};
class Retangulo : public Grafico {
public:
void desenhar() override;
};
// Classe canvas
class Tela {
public:
void adicionar(const std::string& tipo);
void pintar() const;
~Tela();
private:
std::vector<Grafico*> graficos;
};
// Funções utilitárias
TipoGrafico stringParaTipo(const std::string& s);
Grafico* criarGrafico(const std::string& tipo);
Grafico.cpp
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include "Grafico.hpp"
void Circulo::desenhar() { std::cout << "Desenhando círculo...\n"; }
void Triangulo::desenhar() { std::cout << "Desenhando triângulo...\n"; }
void Retangulo::desenhar() { std::cout << "Desenhando retângulo...\n"; }
void Tela::adicionar(const std::string& tipo) {
Grafico* g = criarGrafico(tipo);
if (g)
graficos.push_back(g);
}
void Tela::pintar() const {
for (Grafico* g : graficos)
g->desenhar();
}
Tela::~Tela() {
for (Grafico* g : graficos)
delete g;
}
TipoGrafico stringParaTipo(const std::string& s) {
std::string t = s;
std::transform(s.begin(), s.end(), t.begin(),
[](unsigned char c) { return std::tolower(c);});
if (t == "circulo") return TipoGrafico::circulo;
if (t == "triangulo") return TipoGrafico::triangulo;
if (t == "retangulo") return TipoGrafico::retangulo;
return TipoGrafico::circulo;
}
Grafico* criarGrafico(const std::string& tipo) {
switch (stringParaTipo(tipo)) {
case TipoGrafico::circulo: return new Circulo;
case TipoGrafico::triangulo: return new Triangulo;
case TipoGrafico::retangulo: return new Retangulo;
default: return nullptr;
}
}
demo3.cpp
#include <string>
#include "Grafico.hpp"
void teste() {
Tela tela;
tela.adicionar("circulo");
tela.adicionar("triangulo");
tela.adicionar("retangulo");
tela.pintar();
}
int main() {
teste();
}
Resultados do Teste
Saída mostra desenho de cada gráfico.
Respostas às Questões
- Questão 1: Composição: std::vector<Grafico*>; Herança: classes derivadas.
- Questão 2: (1) Sem virtual, desenhar() base seria chamada, sem efeito. (2) Vetor de ponteiros permite polimorfismo; vetor de objetos não. (3) Destrutor virtual evita leaks.
- Questão 3: Para adicionar estrela: adicionar classe Estrela, atualizar enum, stringParaTipo(), criarGrafico().
- Questão 4: (1) Objetos criados com new são liberados pelo destrutor de Tela. (2) Vantagens: gerenciamento unificado; desvantagens: risco de leaks e complexidade.
Tarefa 4: Fábrica de Brinquedos com Padrão Factory
Código Fonte
Brinquedo.hpp
#pragma once
#include <string>
enum class TipoBrinquedo
{
DESCONHECIDO,
CACHORRO,
GATO,
URSO,
};
class Brinquedo
{
public:
Brinquedo(const std::string& nome = "Nome Padrão", const std::string& material = "Material Padrão", const std::string& audio = "Música Padrão");
virtual ~Brinquedo();
virtual void iniciar() = 0;
virtual void agir() = 0;
virtual void tocarAudio() = 0;
virtual void desligar() = 0;
void exibirInfo();
const std::string& obterNome() const { return nome; }
protected:
std::string nome;
std::string material;
std::string audio;
TipoBrinquedo tipo;
};
Brinquedo.cpp
#include "Brinquedo.hpp"
#include <iostream>
Brinquedo::Brinquedo(const std::string& nome0, const std::string& material0, const std::string& audio0) :
nome{ nome0 }, material{ material0 }, audio{ audio0 }, tipo{TipoBrinquedo::DESCONHECIDO} { }
Brinquedo::~Brinquedo() = default;
void Brinquedo::exibirInfo()
{
std::cout << nome << '\t' << material << '\t';
switch (tipo)
{
case TipoBrinquedo::CACHORRO:
std::cout << "Cachorro\t";
break;
case TipoBrinquedo::GATO:
std::cout << "Gato\t";
break;
case TipoBrinquedo::URSO:
std::cout << "Urso\t";
break;
default:
std::cout << "Tipo Desconhecido\t";
}
std::cout << audio << "\n";
}
BrinquedoCachorro.hpp
#pragma once
#include "Brinquedo.hpp"
class BrinquedoCachorro : public Brinquedo
{
public:
BrinquedoCachorro(const std::string& nome = "Cachorro Amigo", const std::string& material = "Algodão", const std::string& audio = "Música Padrão");
void iniciar() override;
void agir() override;
void tocarAudio() override;
void desligar() override;
private:
void latir();
static constexpr const char* latido = "Au au!";
};
BrinquedoCachorro.cpp
#include "BrinquedoCachorro.hpp"
#include <iostream>
BrinquedoCachorro::BrinquedoCachorro(const std::string& nome0, const std::string& material0, const std::string& audio0) : Brinquedo(nome0,material0,audio0)
{
tipo = TipoBrinquedo::CACHORRO;
}
void BrinquedoCachorro::iniciar()
{
std::cout << "Oi! Meu nome é " << nome << ". Vamos brincar! ";
latir();
std::cout << std::endl;
}
void BrinquedoCachorro::agir()
{
std::cout << nome << " está abanando o rabo!\n";
}
void BrinquedoCachorro::tocarAudio()
{
std::cout << "Tocando: " << audio << '\n';
}
void BrinquedoCachorro::desligar()
{
std::cout << "Adeus! ";
latir();
std::cout << '\n';
}
void BrinquedoCachorro::latir()
{
std::cout << latido;
}
Notas: Outras classes como BrinquedoGato, BrinquedoUrso seguem estrutura similar.
FabricaBrinquedos.hpp
#include "Brinquedo.hpp"
#include "BrinquedoUrso.hpp"
#include "BrinquedoCachorro.hpp"
#include "BrinquedoGato.hpp"
#include <string>
#include <vector>
class FabricaBrinquedos
{
public:
FabricaBrinquedos(const std::string& titulo0);
~FabricaBrinquedos();
void exibir() const;
void acao() const;
void tocarMusica() const;
void adicionarBrinquedo(const std::string& tipo = "desconhecido", const std::string& nome = "Nome Padrão",
const std::string& material = "Material Padrão", const std::string& audio = "Música Padrão");
void removerBrinquedo(const std::string& alvo);
private:
std::string titulo;
std::vector<Brinquedo*> brinquedos;
};
TipoBrinquedo stringParaTipoBrinquedo(const std::string& s);
FabricaBrinquedos.cpp
#include "FabricaBrinquedos.hpp"
#include <algorithm>
#include <cctype>
#include <iostream>
FabricaBrinquedos::FabricaBrinquedos(const std::string& titulo0) : titulo{titulo0} { }
FabricaBrinquedos::~FabricaBrinquedos()
{
for (Brinquedo* b : brinquedos)
delete b;
}
TipoBrinquedo stringParaTipoBrinquedo(const std::string& s)
{
std::string t = s;
std::transform(s.begin(), s.end(), t.begin(), [](unsigned char c) {return std::tolower(c); });
if (t == "cachorro")
return TipoBrinquedo::CACHORRO;
if (t == "gato")
return TipoBrinquedo::GATO;
if (t == "urso")
return TipoBrinquedo::URSO;
return TipoBrinquedo::DESCONHECIDO;
}
void FabricaBrinquedos::exibir() const
{
std::cout << "Informações dos Brinquedos: \n";
if (brinquedos.size() == 0)
{
std::cout << "fábrica vazia!";
return;
}
for (Brinquedo* b : brinquedos)
b->exibirInfo();
}
void FabricaBrinquedos::acao() const
{
for (auto b : brinquedos)
b->agir();
}
void FabricaBrinquedos::tocarMusica() const
{
for (auto b : brinquedos)
b->tocarAudio();
}
void FabricaBrinquedos::adicionarBrinquedo(const std::string& tipo, const std::string& nome, const std::string& material, const std::string& audio)
{
for (Brinquedo* b : brinquedos)
{
if (b->obterNome() == nome)
{
std::cerr << nome << " já existe!\n";
return;
}
}
Brinquedo* novoBrinquedo;
switch (stringParaTipoBrinquedo(tipo))
{
case TipoBrinquedo::CACHORRO:
novoBrinquedo = new BrinquedoCachorro(nome, material, audio);
break;
case TipoBrinquedo::GATO:
novoBrinquedo = new BrinquedoGato(nome, material, audio);
break;
case TipoBrinquedo::URSO:
novoBrinquedo = new BrinquedoUrso(nome, material, audio);
break;
default:
std::cerr << "Falha ao adicionar brinquedo: " << nome << " Tipo desconhecido\n";
return;
}
brinquedos.push_back(novoBrinquedo);
std::cout << "Sucesso ao adicionar brinquedo: " << nome << '\n';
}
void FabricaBrinquedos::removerBrinquedo(const std::string& alvo)
{
for (auto it = brinquedos.begin(); it != brinquedos.end(); it++)
{
if (*it && (*it)->obterNome() == alvo)
{
delete *it;
brinquedos.erase(it);
std::cout << "Remoção bem-sucedida\n";
return;
}
}
std::cerr << "alvo não encontrado";
}
principal.cpp
#include <iostream>
#include <string>
#include "FabricaBrinquedos.hpp"
void teste1()
{
FabricaBrinquedos fabrica("Minha Fábrica de Brinquedos");
fabrica.adicionarBrinquedo("gato", "kitty", "algodão", "Miau Matinal");
fabrica.adicionarBrinquedo("CACHORRO", "Bruce", "Plástico", "Erica");
fabrica.adicionarBrinquedo("Urso", "Teddy", "Algodão", "Hotel California");
fabrica.exibir();
fabrica.acao();
fabrica.tocarMusica();
fabrica.removerBrinquedo("kitty");
fabrica.exibir();
}
int main()
{
teste1();
}
Resultados do Teste
Demonstra criação, ação e remoção de brinquedos.
Respostas às Questões
- Questão 1: Composição em FabricaBrinquedos (vector de ponteiros); Herança nas classes de brinquedo.
- Questão 2: (1) Saída vazia sem virtual. (2) Vector de ponteiros permite ploimorfismo. (3) Destrutor não virtual causa leaks.
- Questão 3: Para adicionar estrela: adicionar classe BrinquedoEstrela, atualizar enum, stringParaTipoBrinquedo(), adicionarBrinquedo().
- Questão 4: (1) Objetos liberados pelo destrutor de FabricaBrinquedos. (2) Vantagens: gerenciamento centralizado; desvantagans: riscos de memória.