如何优雅的实现一个C++分数类(Rational) ?

什么样的Rational类实现算是优雅?

在我看来它应该符合以下几个特点

1.符合面向对象的封装特性,数据隐藏(因为只有一个类,没有考虑封装和多态)。

2.声明与实现相分离,接口清晰,自然,既有足够的基本功能又不冗余。

3.代码简洁清晰

4.最好不加注释就能看懂,变量名,方法名能够见名知义

5.do not repeat yourself,不要做重复的工作

6.出错时抛出异常,中止程序,而不是将错误掩盖在内部实现之中,埋下隐患。

下面的这个Rational实现我认为是比较优雅的,它符合上面的5个特点

1.public ,private展示了其数据隐藏特点,分子,分母类的使用者不可访问

2.提供了加减乘除相反数倒数相等的基本操作,而且重载了+,-,*,/操作符,使用时可以更加清晰自然

3.每个方法的实现 本身就不复杂,gcd函数的实现更是非常简洁

4.方法名,分子分母基本能够见名知义

5.减法用加上相反数来实现,除法用乘以倒数来实现,能够体现这一点

6.分母为零时,分子为零求倒数时抛出异常

rational.h

#ifndef Rational_
#define Rational_
#include "myException.h"
#include<iostream>
using namespace std;

class Rational{
public:
    Rational(int x, int y);//传入分子分母构造分数
    Rational(string s);//从字符串构造分数
    Rational nega();//相反数
    Rational reci();//倒数
    bool isEqual(Rational that);
    Rational add(Rational that);//加法
    Rational sub(Rational that);//减法
    Rational mul(Rational that);//乘法
    Rational div(Rational that);//除法
    void Output(ostream &out)const;//输出方法
    int getNumer(){return numer;}
    int getDenom(){return denom;}
private:
    int numer;//分子
    int denom;//分母
    int gcd(int a, int b) const { return (b==0) ? a : gcd(b, a % b); }//最大公约数
};
#endif // Rational_define

Rational.cpp

#include "rational.h"
#include "myException.h"
#include<cstdlib>

using namespace std;

Rational::Rational(int x, int y){//传入分子分母构造分数
    if(y==0) throw new illegalDenominatorValue();
    numer = x;
    denom = y;
}
//从字符串构造分数
Rational::Rational(string s){
    int pos = s.find('/',0);
    if(pos <= 0 || pos >= s.length()-1) throw new illegalInputExpression();
    numer = atoi( s.substr(0,pos).c_str() );
    denom = atoi( s.substr(pos+1,s.length()-pos-1).c_str() );
    if(denom == 0 ) throw new illegalDenominatorValue();
}
//相反数
Rational Rational::nega(){
    return Rational(-numer, denom);
}
//倒数
Rational Rational::reci(){
    if(numer == 0) throw new NoReciprocal();
    return Rational(denom,numer);
}
//相等
 bool Rational::isEqual(Rational that){
    return numer * that.denom == denom * that.numer;
 }
//加法
Rational Rational::add(Rational that){
    return Rational(numer * that.denom + denom * that.numer,denom * that.denom);
}
//减法
Rational Rational::sub(Rational that){
    return add(that.nega());
}
//乘法
Rational Rational::mul(Rational that){
    return Rational(numer  *  that.numer, denom * that.denom);
}
//除法
Rational Rational::div(Rational that){
    return mul(that.reci());
}
//输出
 void Rational::Output(ostream &out)const{
        int c = gcd(numer,denom);
       // int c = 1;
        out<<numer/c<<"/"<<denom/c;
 }
 //输出操作符重载
ostream& operator<<(ostream& out, const Rational& r){
    r.Output(out);
    return out;
}
//操作符重载
Rational operator +(Rational a, Rational b){
    return a.add(b);
}
Rational operator -(Rational a, Rational b){
    return a.sub(b);
}
Rational operator *(Rational a, Rational b){
    return a.mul(b);
}
Rational operator /(Rational a, Rational b){
    return a.div(b);
}

myException.h

// exception classes for various error types

#ifndef myExceptions_
#define myExceptions_
#include <string>
#include<iostream>
using namespace std;

// illegal Denominator value
class illegalDenominatorValue
{
   public:
      illegalDenominatorValue(string theMessage = "Denominator must be nonezero!"){
          message = theMessage;
      }
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// illegal input Expression
class illegalInputExpression
{
   public:
      illegalInputExpression(string theMessage = "Illegal  input Expression"){
          message = theMessage;
      }
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};
//倒数
class NoReciprocal
{
   public:
      NoReciprocal(string theMessage = "No Reciprocal with numerator=0"){
          message = theMessage;
      }
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};
#endif

test.cpp

#include "rational.h"
#include "Rational.cpp"
using namespace std;
int main(){
    Rational a(1,2);
    Rational b("1/4");
    //Rational c("1/0");
    //Rational c("1/");
    cout<<"a =  \t"<<   a       <<endl;
    cout<<"b =  \t"<<   b       <<endl;
    cout<<"-a = \t"<<   a.nega()<<endl;
    cout<<"1/b =\t"<<   b.reci()<<endl;

    cout<<"a.add(b) =\t"<<   a.add(b)<<endl;
    cout<<"a.sub(b) =\t"<<   a.sub(b)<<endl;
    cout<<"a.mul(b) =\t"<<   a.mul(b)<<endl;
    cout<<"a.div(b) =\t"<<   a.div(b)<<endl;

    cout<<"a+b =\t"<<   a+b<<endl;
    cout<<"a-b =\t"<<   a-b<<endl;
    cout<<"a*b =\t"<<   a*b<<endl;
    cout<<"a/c =\t"<<   a/b<<endl;

    return 0;
}

作为一个例子,我们来看如何使用这个类解决“求解形如a/b x+ c/d = e/f的一元一次分式方程”的问题

#include "rational.h"
#include "Rational.cpp"

using namespace std;

int main(){
while(1){
    cout << "输入方程:形如a/b x + d/c = f/e"<<endl;
    char Arr[50];
    cin.getline(Arr, 50);
    string equation(Arr);
   // cout << equation <<endl;
//   a/b x + d/c = f/e
    int pos_x = equation.find('x',0);
    int pos_p = equation.find('+',0);
    int pos_e = equation.find('=',0);

    Rational coe( equation.substr(0,pos_x) );// a/b
    Rational b( equation.substr(pos_p+1,pos_e - pos_p) );// d/c
    Rational c( equation.substr(pos_e+1,equation.length()-pos_e) );// f/e

 //   cout<<"coe = "<<coe<<endl;
 //   cout<<"b = "<<b<<endl;
 //   cout<<"c = "<<c<<endl;
    if(coe.getNumer() == 0 && b.isEqual(c)) cout<<"x ∈ Q"<<endl;
    else if(coe.getNumer() == 0 && !b.isEqual(c)) cout<<"无解"<<endl;
    else  cout<<"解得 x = "<<(c-b)/coe<<endl<<endl;

    system("pause");
    system("cls");
}
}

代码如下

main.cpp

#include "rational.h"
#include "Rational.cpp"

using namespace std;

int main(){
while(1){
    cout << "输入方程:形如a/b x + d/c = f/e"<<endl;
    char Arr[50];
    cin.getline(Arr, 50);
    string equation(Arr);
   // cout << equation <<endl;
//   a/b x + d/c = f/e
    int pos_x = equation.find('x',0);
    int pos_p = equation.find('+',0);
    int pos_e = equation.find('=',0);

    Rational coe( equation.substr(0,pos_x) );// a/b
    Rational b( equation.substr(pos_p+1,pos_e - pos_p) );// d/c
    Rational c( equation.substr(pos_e+1,equation.length()-pos_e) );// f/e

 //   cout<<"coe = "<<coe<<endl;
 //   cout<<"b = "<<b<<endl;
 //   cout<<"c = "<<c<<endl;
    if(coe.getNumer() == 0 && b.isEqual(c)) cout<<"x ∈ Q"<<endl;
    else if(coe.getNumer() == 0 && !b.isEqual(c)) cout<<"无解"<<endl;
    else  cout<<"解得 x = "<<(c-b)/coe<<endl<<endl;

    system("pause");
    system("cls");
}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-16 15:40:18

如何优雅的实现一个C++分数类(Rational) ?的相关文章

Java 有理数类 分数类 Rational类的设计与实现

要实现Rational类的加减乘除,要实现其可比较性,要覆盖toString()方法,要实现不同数据类型的转换等. 1 package chapter14; 2 3 public class Rational extends Number implements Comparable { 4 private long numerator=0; 5 private long denominator=1; 6 7 public Rational(){ 8 this(0,1); 9 } 10 publi

实验11:Problem C: 分数类的乘法

Home Web Board ProblemSet Standing Status Statistics Problem C: 分数类的乘法 Problem C: 分数类的乘法 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 650  Solved: 477[Submit][Status][Web Board] Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:传入两个参数n和m,表示n/m:分数在

Problem F: 分数类的类型转换

Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:传入两个参数n和m,表示n/m:分数在构造时立即转化成最简分数. 2. show()函数:分数输出为"a/b"或"-a/b"的形式,a.b都是无符号整数.若a为0或b为1,只输出符号和分子,不输出"/"和分母. 3. double类型转换函数:用分子除以分母,得到的小数.注意:分子为0时不要输出为"-0" ----------

Problem C: 分数类的乘法

Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:传入两个参数n和m,表示n/m:分数在构造时立即转化成最简分数. 2. show()函数:分数输出为"a/b"或"-a/b"的形式,a.b都是无符号整数.若a为0或b为1,只输出符号和分子,不输出"/"和分母. 3. 在分数类上重载乘法运算符,进行分数的乘法运算 ------------------------------------------

实验11:Problem A: 分数类的输出

注意如果是负数,要把负号放在分子上 Home Web Board ProblemSet Standing Status Statistics Problem A: 分数类的输出 Problem A: 分数类的输出 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1453  Solved: 574[Submit][Status][Web Board] Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:

实验11:Problem D: 分数类的模板数组类

在默认构造函数里面,分母的默认值不能为0!! Home Web Board ProblemSet Standing Status Statistics Problem D: 分数类的模板数组类 Problem D: 分数类的模板数组类 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 509  Solved: 350[Submit][Status][Web Board] Description 封装一个模板数组类Array,支持一下操作: 1. 构造函

实验11:Problem B: 分数类的类型转换

Home Web Board ProblemSet Standing Status Statistics Problem B: 分数类的类型转换 Problem B: 分数类的类型转换 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 579  Solved: 431[Submit][Status][Web Board] Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:传入两个参数n和m,表示n/m

第四周项目二-分数类的雏形

[项目2-分数类的雏形] C++中提供了多种基本的数据类型.实际上,这些远不能满足我们的需求,如复数(第10章的例子大多是处理复数),再如分数.我们可以自定义类支持这些数据类型. 本任务将设计一个简单的分数类,完成对分数的几个运算.一则巩固基于对象编程的方法,二则也为运算符重载等积累些感性认识. 分数类的声明为: class CFraction { private: int nume; // 分子 int deno; // 分母 public: CFraction(int nu=0,int de

Problem A: 分数类的输出

Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作: 1. 构造:传入两个参数n和m,表示n/m:分数在构造时立即转化成最简分数. 2. show()函数:分数输出为"a/b"或"-a/b"的形式,a.b都是无符号整数.若a为0或b为1,只输出符号和分子,不输出"/"和分母. --------------------------------------------------------------------