Implementação de Calculadora de Notas com Composição e Herança em C++

  1. Implementação com Composição

Esta seção apresenta uma implementação da classe GradeCalc utilizando o conceito de composição para gerenciar notas acadêmicas.

#pragma once

#include <vector>
#include <array>
#include <string>

class GradeCalc {
public:
    GradeCalc(const std::string &cname);      
    void input(int n);                         // Registra n notas
    void output() const;                       // Exibe as notas
    void sort(bool ascending = false);         // Ordena (decrescente por padrão)
    int min() const;                           // Retorna nota mínima (-1 se não houver notas)
    int max() const;                           // Retorna nota máxima (-1 se não houver notas)
    double average() const;                    // Retorna média (0.0 se não houver notas)
    void info();                               // Mostra informações das notas

private:
    void compute();                            // Estatísticas das notas

private:
    std::string course_name;                  // Nome da disciplina
    std::vector<int> grades;                  // Notas da disciplina
    std::array<int, 5> counts;                // Contagem por faixas ([0,60), [60,70), [70,80), [80,90), [90,100])
    std::array<double, 5> rates;              // Percentual por faixa
    bool is_dirty;                            // Indicador de alteração nas notas
};

#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

#include "GradeCalc.hpp"

GradeCalc::GradeCalc(const std::string &cname): course_name{cname}, is_dirty{true} {
    counts.fill(0);
    rates.fill(0.0);
}

void GradeCalc::input(int n) {
    if(n < 0) {
        std::cerr << "Entrada inválida! Número de alunos não pode ser negativo\n";
        std::exit(1);
    }

    grades.reserve(n);

    int nota;
    for(int i = 0; i < n;) {
        std::cin >> nota;

        if(nota < 0 || nota > 100) {
            std::cerr << "Nota inválida! Deve estar entre 0 e 100\n";
            continue;
        }
        
        grades.push_back(nota);
        ++i;
    }

    is_dirty = true;
}

void GradeCalc::output() const {
    for(auto nota: grades)
        std::cout << nota << ' ';
    std::cout << std::endl;
}
    
void GradeCalc::sort(bool ascending) {
    if(ascending)
        std::sort(grades.begin(), grades.end());
    else
        std::sort(grades.begin(), grades.end(), std::greater<int>());
}

int GradeCalc::min() const {
    if(grades.empty())
        return -1;

    auto it = std::min_element(grades.begin(), grades.end());
    return *it;
}

int GradeCalc::max() const {
    if(grades.empty()) 
        return -1;

    auto it = std::max_element(grades.begin(), grades.end());
    return *it;
}

double GradeCalc::average() const {
    if(grades.empty())
        return 0.0;

    double media = std::accumulate(grades.begin(), grades.end(), 0.0) / grades.size();
    return media;
}

void GradeCalc::info() {
    if(is_dirty)
       compute();

    std::cout << "Disciplina:\t" << course_name << std::endl;
    std::cout << "Média:\t" << std::fixed << std::setprecision(2) << average() << std::endl;
    std::cout << "Máxima:\t" << max() << std::endl;
    std::cout << "Mínima:\t" << min() << 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: " << counts[i] << " alunos\t"
                  << std::fixed << std::setprecision(2) << rates[i]*100 << "%\n";
}

void GradeCalc::compute() {
    if(grades.empty())
        return;

    counts.fill(0);
    rates.fill(0.0);

    for(auto nota:grades) {
        if(nota < 60)
            ++counts[0];
        else if (nota < 70)
            ++counts[1];
        else if (nota < 80)
            ++counts[2];
        else if (nota < 90)
            ++counts[3];
        else
            ++counts[4];
    }

    for(size_t i = 0; i < rates.size(); ++i)
        rates[i] = counts[i] * 1.0 / grades.size();
    
    is_dirty = false;
}

#include <iostream>
#include <string>
#include "GradeCalc.hpp"

void teste() {
    GradeCalc calc("POO");

    std::cout << "Inserindo notas:\n";
    calc.input(5);

    std::cout << "Notas registradas:\n";
    calc.output();

    std::cout << "Notas ordenadas:\n";
    calc.sort(); calc.output();

    std::cout << "*************Estatísticas*************\n";
    calc.info();
}

int main() {
    teste();
}

Análise de Conceitos

Relacionamento de Composição: A classe GradeCalc incorpora objetos de string, vector e array diretamente como membros, representando uma relação "tem um".

Exposição de Interface: Métodos privados como push_back do vector não são acessíveis externamente, mantendo o encapsulamento.

Gerenciamento de Estado: O flag is_dirty otimiza a atualização de estatísticas, evitando recálculos desnecessários.

  1. Implemantação com Herança

Nesta abordagem, a classe GradeCalc herda funcionalidades da std::vector<int> para gerenciar as notas.

#pragma once

#include <array>
#include <string>
#include <vector>

class GradeCalc: private std::vector<int> {
public:
    GradeCalc(const std::string &cname);      
    void input(int n);                        
    void output() const;                      
    void sort(bool ascending = false);        
    int min() const;                          
    int max() const;                          
    double average() const;                   
    void info();                              

private:
    void compute();               

private:
    std::string course_name;     
    std::array<int, 5> counts;   
    std::array<double, 5> rates; 
    bool is_dirty;      
};

#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
#include "GradeCalc.hpp"

GradeCalc::GradeCalc(const std::string &cname): course_name{cname}, is_dirty{true}{
    counts.fill(0);
    rates.fill(0);
}   

void GradeCalc::input(int n) {
    if(n < 0) {
        std::cerr << "Erro: número inválido de alunos\n";
        return;
    }

    this->reserve(n);

    int valor;
    for(int i = 0; i < n;) {
        std::cin >> valor;
        if(valor < 0 || valor > 100) {
            std::cerr << "Nota deve estar entre 0 e 100\n";
            continue;
        }

        this->push_back(valor);
        ++i;
    } 

    is_dirty = true;
}  

void GradeCalc::output() const {
    for(auto nota: *this)
        std::cout << nota << ' ';
    std::cout << std::endl;
} 

void GradeCalc::sort(bool ascending) {
    if(ascending)
        std::sort(this->begin(), this->end());
    else
        std::sort(this->begin(), this->end(), std::greater<int>());
}  

int GradeCalc::min() const {
    if(this->empty())
        return -1;

    return *std::min_element(this->begin(), this->end());
}  

int GradeCalc::max() const {
    if(this->empty())
        return -1;

    return *std::max_element(this->begin(), this->end());
}    

double GradeCalc::average() const {
    if(this->empty())
        return 0.0;

    double media = std::accumulate(this->begin(), this->end(), 0.0) / this->size();
    return media;
}   

void GradeCalc::info() {
    if(is_dirty) 
        compute();

    std::cout << "Curso:\t" << course_name << std::endl;
    std::cout << "Média:\t" << std::fixed << std::setprecision(2) << average() << std::endl;
    std::cout << "Melhor:\t" << max() << std::endl;
    std::cout << "Pior:\t" << min() << std::endl;

    const std::array<std::string, 5> intervalos{"[0, 60) ", 
                                               "[60, 70)", 
                                               "[70, 80)",
                                               "[80, 90)", 
                                               "[90, 100]"};
    
    for(int i = static_cast<int>(intervalos.size())-1; i >= 0; --i)
        std::cout << intervalos[i] << "\t: " << counts[i] << " estudantes\t"
                  << std::fixed << std::setprecision(2) << rates[i]*100 << "%\n";
}

void GradeCalc::compute() {
    if(this->empty())
        return;
    
    counts.fill(0);
    rates.fill(0);

    for(int nota: *this) {
        if(nota < 60)
            ++counts[0];
        else if (nota < 70)
            ++counts[1];
        else if (nota < 80)
            ++counts[2];
        else if (nota < 90)
            ++counts[3];
        else
            ++counts[4];
    }

    for(size_t i = 0; i < rates.size(); ++i)
        rates[i] = counts[i] * 1.0 / this->size();
    
    is_dirty = false;
}

Comparação de Abordagens

A herança privada limita o acesso aos métodos da classe base, diferentemente da composição onde os membros são explicitamente encapsulados.

  1. Sistema de Renderização Gráfica

Este exemplo demonstra polimorfismo através de classes asbtratas e funções virtuais para desenhar formas geométricas.

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>

// Declarações de classes abstratas
class Graph {
public:
    virtual ~Graph() = default;
    virtual void draw() = 0;
};

enum class GraphType {circle, triangle, rectangle};

class Circle : public Graph {
public:
    void draw() override { std::cout << "Desenhando círculo...\n"; }
};

class Triangle : public Graph {
public:
    void draw() override { std::cout << "Desenhando triângulo...\n"; }
};

class Rectangle : public Graph {
public:
    void draw() override { std::cout << "Desenhando retângulo...\n"; }
};

class Canvas {
public:
    void add(const std::string& type);
    void paint() const;
    ~Canvas();

private:
    std::vector<Graph*> graphs;
};

GraphType str_to_GraphType(const std::string& s);
Graph* make_graph(const std::string& type);

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include "Graph.hpp"

void Circle::draw()     { std::cout << "Desenhando círculo...\n"; }
void Triangle::draw()   { std::cout << "Desenhando triângulo...\n"; }
void Rectangle::draw()  { std::cout << "Desenhando retângulo...\n"; }

void Canvas::add(const std::string& type) {
    Graph* figura = make_graph(type);
    if (figura) 
        graphs.push_back(figura);
}

void Canvas::paint() const {
    for (Graph* figura : graphs) 
        figura->draw();   
}

Canvas::~Canvas() {
    for (Graph* figura : graphs) 
        delete figura;
}

GraphType str_to_GraphType(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 == "circle")   
        return GraphType::circle;
    if (t == "triangle") 
        return GraphType::triangle;
    if (t == "rectangle")
        return GraphType::rectangle;

    return GraphType::circle;
}

Graph* make_graph(const std::string& type) {
    switch (str_to_GraphType(type)) {
    case GraphType::circle:     return new Circle;
    case GraphType::triangle:   return new Triangle;
    case GraphType::rectangle:  return new Rectangle;
    default: return nullptr;
    }
}

#include <string>
#include "Graph.hpp"

void teste() {
    Canvas tela;

    tela.add("circle");
    tela.add("triangle");
    tela.add("rectangle");
    tela.paint();
}

int main() {
    teste();
}

  1. Sistema de Brinquedos Eletrônicos

Modelagem orientada a objetos aplicada a um sistema de controle de brinquedos interativos.

#include<iostream>
#include<vector>
#include<string>

class Toy{
private:
    std::string nome;
    std::string tipo;
    double preco;
    int quantidade;
    
public:
    Toy(std::string n,std::string t,double p,int q):
        nome(n),tipo(t),preco(p),quantidade(q){}
    virtual ~Toy()=default;
    
    virtual void mostrar()const {
        std::cout<<"Nome:"<<nome<<" Tipo:"<<tipo<<" Preço:"
                  <<preco<<" R$ Qtde:"<<quantidade<<std::endl;
    };
    virtual void habilidade()const =0;
};

class DanceToy:public Toy{
public:
    DanceToy(std::string nome,double preco,int qtde):
        Toy(nome,"Dança",preco,qtde){}
    void habilidade()const override{
        std::cout<<"Habilidade Especial: Dança elegante."<<std::endl;
    }
};

class MusicToy:public Toy{
public:
    MusicToy(std::string nome,double preco,int qtde):
        Toy(nome,"Música",preco,qtde){}
    void habilidade()const override{
        std::cout<<"Habilidade Especial: Toca música."<<std::endl;
    }
};

class LightToy:public Toy{
public:
    LightToy(std::string nome,double preco,int qtde):
        Toy(nome,"Luz",preco,qtde){}
    void habilidade()const override{
        std::cout<<"Habilidade Especial: Emite luz."<<std::endl;
    }
};

class ToyFactory{
public:
    void incluir(Toy* brinquedo);
    void listar()const;
    ~ToyFactory();
    
private:
    std::vector<Toy*> estoque;
};

#include<iostream>
#include<vector>
#include<string>
#include "Toy.hpp" 

void ToyFactory::incluir(Toy* brinquedo) {
    estoque.push_back(brinquedo);
} 

void ToyFactory::listar() const {
    std::cout<<"\nBrinquedos na fábrica:\n";
    for(auto* item:estoque){
        item->mostrar();
        item->habilidade();
        std::cout<<std::endl;
    }
}

ToyFactory::~ToyFactory() {
    for(auto &item:estoque) delete item;
}

#include<iostream>
#include "Toy.hpp"

int main() {
    ToyFactory fabrica;
    fabrica.incluir(new DanceToy("Urso",88.5,6));
    fabrica.incluir(new MusicToy("Pássaro",66.0,10));
    fabrica.incluir(new LightToy("Gato",77.3,15));
    
    fabrica.listar();
    return 0;
}

Tags: C++ Orientação a Objetos composicao Herança Polimorfismo

Publicado em 8-26 17:07