【问题标题】:C++ overloading * to multiply fractions of different typesC++ 重载 * 以乘以不同类型的分数
【发布时间】:2021-01-19 15:45:00
【问题描述】:

我有一个带有私有类型 N(分子)和 D(分母)的分数类。这也允许类型为不同的数字类型。我还制作了一个覆盖星号的运算符函数,以便我可以执行 x*y ,其中 x 和 y 表示分数对象。但是,除非两者都只使用一种类型,否则它不起作用。我怎样才能解决这个问题?为该函数尝试了单独的模板,但似乎很复杂。另外,如果有办法在模板中说只有数字类型(类似于 java 中的 Number 类),那将不胜感激:)

using namespace std;

template <typename N, typename  D> class Fraction {
private:
    N num;
    D denom;

public:
    Fraction(N numerator = 1, D denominator = 0) {
        assert(numerator != 0);
        this -> num   =     numerator;
        this -> denom =     denominator;
    }
    inline N getNumerator()    { return num;   }
    inline D getDenominator()  { return denom; }

    void setNumerator(N numerator)      { num = numerator;      }
    void setDenominator(D denominator)  { denom = denominator;  }

    friend Fraction operator*(Fraction& f, Fraction& g) {
        N numerator;
        D denominator;
        numerator = f.getNumerator() * g.getNumerator();
        denominator = f.getDenominator() * g.getDenominator();

        return Fraction(numerator, denominator);
    }

    friend Fraction operator*(const Fraction& f, int n) {
        return Fraction(n*f.getNumerator, f.getDenominator());
    }

    friend ostream& operator<<(ostream& os, const Fraction& f ) {
        return os << f.num << "/" << f.denom << endl;
    }
};

【问题讨论】:

  • 您可能对std::ratio 感兴趣,尽管它与编译时间分数有关

标签: c++ templates operator-overloading


【解决方案1】:

您的操作员必须有四个模板参数:

template <typename N1, typename  D1, typename N2, typename  D2> 
Fraction<N1, D1> operator*(Fraction<N1, D1>& f, Fraction<N2, D2>& g) {
    N1 numerator;
    D1 denominator;
    numerator = f.getNumerator() * g.getNumerator();
    denominator = f.getDenominator() * g.getDenominator();

    return Fraction<N1, D1>(numerator, denominator);
}

(请注意,不需要将运算符设为friend。)

【讨论】:

  • 啊谢谢你,你知道为什么/我应该在未来避免朋友吗?几乎一直使用它
  • 还有四舍五入的结果有没有办法在不强制类型的情况下准确存储结果?
  • @Rubbs:乘法没有四舍五入?通常,“准确”存储结果首先要了解准确性对您的问题意味着什么。没有计算机可以包含圆周率的所有数字,但大多数都足够准确。
  • @Rubbs:friend 的目的是允许非公共成员访问。如果它不需要访问非公共成员,那么它不必是friend。至于四舍五入,我无法改进@MSalters 的回复。
  • @Rubbs:我应该补充一点,如果它不需要是friend,那么它不应该是。良好的封装意味着不提供超出要求的访问权限。邮递员应该可以进入我前门的邮筒;把我家的钥匙给他也行,但会招来麻烦。
【解决方案2】:
template<class N, class D=N>
class Fraction {
    N numerator = 0;
    D denominator = 1;

    constexpr Fraction& operator*=(Fraction const& rhs)&{
      numerator *= rhs.numerator;
      denominator *= rhs.denominator;
      return *this;
    }
    constexpr Fraction& operator*=(int rhs)&{
      numerator *= rhs;
      return *this;
    }
    template<class N2, class D2>
    constexpr Fraction& operator*=(Fraction<N2, D2> const& rhs)&{
      numerator *= rhs.numerator;
      denominator *= rhs.denominator;
      return *this;
    }
    friend constexpr Fraction operator*(Fraction lhs, Fraction const& rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }
    template<class N2, class D2>
    friend constexpr Fraction operator*(Fraction lhs, Fraction<N2,D2> const& rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }

    friend constexpr Fraction operator*(Fraction lhs, int rhs) {
      lhs*=rhs;
      return std::move(lhs);
    }
    friend constexpr Fraction operator*(int lhs, Fraction rhs) {
      rhs*=lhs;
      return std::move(rhs);
    }

    friend ostream& operator<<(ostream& os, const Fraction& f ) {
        return os << f.numerator << "/" << f.denominator << endl;
    }
};
// deduction guide.  If you are mixing types, you have to pass in
// the type explicitly.  This is intentional.
template<class T>
Fraction(T, T)->Fraction<T,T>;

这是您尝试做的一个不错的实现。

缺少访问器是故意的;这是毫无意义的样板。默认分数是0/1 而不是1/0

非模板运算符很有用,因为它们允许

Fraction x{5,2};
x *= {2,3};
auto y = x*{1,2};

等等。

这种设计有点面向未来,是编写算术类型时遵循的一个很好的默认模式。它并不完美(它不支持对a*b 的结果使用推导类型),但它解决了一堆你不知道的问题。

假设ND 是某种bigint(存储在堆上,但移动效率高);那么上面的构造将允许*的长链发生而不重新分配;它将“免费”重复使用临时缓冲区。

Fraction<BigInt> a,b,c,d,e;
auto r = a*b*c*d*e;

a*b 复制a,然后进行乘法运算,然后移动结果,然后在没有副本的情况下执行*c,然后在没有副本的情况下执行*d,然后在没有副本的情况下执行*e,然后将其存储ra 的原始修改副本。

constexpr 垃圾邮件是因为在编译时做分数数学是一件合理的事情。

【讨论】:

  • 扣分指南是做什么的?以前从未见过:0
  • 为什么在模板中使用类而不是类型名?
  • @Rubbs:演绎指南允许您使用Fraction x{5,2}; 而不是Fraction&lt;int,int&gt; x{5,2};。至于模板中的class,那是个人喜好。
  • 我已经可以做到了吗?或者,如果您将构造函数定义为显式,这是否很重要?
  • @Rubbs 隐式演绎公会增加。由于我个人认为值得避免意外生成非均匀分数的风险,所以我阻止了它;唯一的指南要求分子和分母类型匹配。当你想要不同的类型时,你可以这样做,但你必须明确。根据我的经验,当您处理非均匀类型的数字时,会发生太多令人惊讶的事情,以使其保持沉默。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 2020-02-27
  • 2016-11-20
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
相关资源
最近更新 更多