Desafio C++
Classe Fração com operadores aritméticos, comparação e saída
Enunciado
Implemente uma classe Fracao que representa números racionais com numerador e denominador inteiros. Sobrecarregue operator+, operator-, operator*, operator/ para realizar as operações e simplificar o resultado. Implemente operator<=> para comparação e operator<< para imprimir no formato numerador/denominador. No main, demonstre as operações e comparações.
Requisitos
- A classe
Fracaodeve ter membrosnumeden(int) e garantir que o denominador nunca seja zero. - Os operadores aritméticos devem retornar uma nova
Fracaosimplificada (usarstd::gcd). - O
operator<=>deve comparar frações pelo valor real (multiplicação cruzada). - O
operator<<deve imprimir no formatonum/den. - O programa deve exibir o resultado das quatro operações e duas comparações.
Código inicial
#include <iostream>
#include <numeric>
#include <compare>
#include <stdexcept>
class Fracao {
public:
int num, den;
Fracao(int n = 0, int d = 1) : num(n), den(d) {
if (den == 0) throw std::invalid_argument("denominador zero");
simplificar();
}
// TODO: operadores aritméticos, <=> e <<
private:
void simplificar() {
int mdc = std::gcd(num, den);
num /= mdc;
den /= mdc;
if (den < 0) { num = -num; den = -den; }
}
};
// TODO: implementar operadores
int main() {
Fracao a(1, 2);
Fracao b(1, 3);
std::cout << "a + b = " << a + b << '\n';
std::cout << "a - b = " << a - b << '\n';
std::cout << "a * b = " << a * b << '\n';
std::cout << "a / b = " << a / b << '\n';
std::cout << "a < b? " << (a < b) << '\n';
std::cout << "a == b? " << (a == b) << '\n';
return 0;
}
Saída esperada
a + b = 5/6
a - b = 1/6
a * b = 1/6
a / b = 3/2
a < b? 0
a == b? 0
Ver dica
Para somar: (num*b.den + b.num*den) / (den*b.den). Para comparar, use num * b.den <=> b.num * den. Lembre-se de simplificar no construtor.
Mostrar solução
#include <iostream>
#include <numeric>
#include <compare>
#include <stdexcept>
class Fracao {
public:
int num, den;
Fracao(int n = 0, int d = 1) : num(n), den(d) {
if (den == 0) throw std::invalid_argument("denominador zero");
simplificar();
}
// Operador de soma
Fracao operator+(const Fracao& o) const {
return Fracao(num * o.den + o.num * den, den * o.den);
}
// Operador de subtração
Fracao operator-(const Fracao& o) const {
return Fracao(num * o.den - o.num * den, den * o.den);
}
// Operador de multiplicação
Fracao operator*(const Fracao& o) const {
return Fracao(num * o.num, den * o.den);
}
// Operador de divisão
Fracao operator/(const Fracao& o) const {
return Fracao(num * o.den, den * o.num);
}
// Operador three-way para comparação
auto operator<=>(const Fracao& o) const {
return num * o.den <=> o.num * den;
}
// Igualdade explícita (gerada por <=>? Não, precisamos definir)
bool operator==(const Fracao& o) const {
return num == o.num && den == o.den;
}
// Operador de saída
friend std::ostream& operator<<(std::ostream& os, const Fracao& f);
private:
void simplificar() {
int mdc = std::gcd(num, den);
num /= mdc;
den /= mdc;
if (den < 0) { num = -num; den = -den; }
}
};
std::ostream& operator<<(std::ostream& os, const Fracao& f) {
os << f.num << '/' << f.den;
return os;
}
int main() {
Fracao a(1, 2);
Fracao b(1, 3);
std::cout << "a + b = " << a + b << '\n';
std::cout << "a - b = " << a - b << '\n';
std::cout << "a * b = " << a * b << '\n';
std::cout << "a / b = " << a / b << '\n';
std::cout << "a < b? " << (a < b) << '\n';
std::cout << "a == b? " << (a == b) << '\n';
return 0;
}
Passo a passo
- A classe
Fracaoarmazenanumeden(int). O construtor verifica denominador zero, lança exceção e chamasimplificar(). simplificar()usastd::gcdpara dividir numerador e denominador pelo máximo divisor comum e ajusta o sinal para o denominador ser positivo.- Os operadores
+,-,*,/são funções membro constantes que criam uma novaFracaocom o resultado e deixam o construtor simplificar. - O operador
<=>compara os valores reais via multiplicação cruzada:num * o.den <=> o.num * den. Isso evita divisão e mantém precisão inteira. - Definimos
operator==explicitamente porque<=>não gera==automaticamente (é necessário definir ambos ou usar= defaultno<=>). operator<<é uma função amiga que imprimenum/den.- No
main, testamos as quatro operações e duas comparações.
Por que funciona
A sobrecarga de operadores permite que Fracao se comporte como um número. As operações aritméticas seguem as regras matemáticas e simplificam o resultado. A comparação via <=> usa multiplicação cruzada, que é correta para frações. O operador << integra a classe com std::cout.
Erros comuns
- Esquecer de simplificar no construtor: resultados como
2/4em vez de1/2. - Não tratar denominador zero: lançar exceção ou evitar divisão por zero.
- Comparar diretamente
num < o.numsem multiplicação cruzada: isso compararia apenas numeradores, o que está errado. - Não definir
operator==quando se usa<=>: o compilador pode não gerar==automaticamente se<=>for definido pelo usuário; é preciso defini-lo ou usar= defaultno<=>.
Outra forma de resolver
Usar = default no <=> e == se a comparação membro a membro for suficiente (não é o caso para frações, pois 1/2 e 2/4 são iguais em valor mas diferentes membro a membro). Para frações, a multiplicação cruzada é necessária.
Saída esperada
a + b = 5/6
a - b = 1/6
a * b = 1/6
a / b = 3/2
a < b? 0
a == b? 0