实现数学函数C++的乘法运算

Implementing multiplication operator for mathematical functions C++

本文关键字:运算 C++ 函数 实现      更新时间:2023-10-16

我有以下抽象基类:

class Function {
 virtual double Eval(double x) const = 0;
};     

我希望能够在我的主文件中使用像f*g或f->运算符*(g)这样的表达式,其中f和g是Function类的具体对象,例如,当我想计算一个定积分时,我可以写:

AnIntegrationMethod(f*g);

我提出的一个相当简单的方法是声明一个类Product(只显示了头文件,实现很明显):

class Product: public Function {
 public: Product(Function *g, Function *f); 
  ~Product(); 
  virtual double Eval(double x) const; //return _g->Eval(x)*_f->Eval(x)
 private: Function *_g; Function *_f;
 };

然后在我的任何功能

#include "Product.h"
class ConcreteFunction: public Function {
 public: 
   ConcreteFunction(); 
  ~ConcreteFunction(); 
  virtual double Eval(double x) const;
 Function* operator*(Function *f) {return new Product(this, f);}
 };

这实际上适用于简单的东西,但问题是运算符*只在基类的单个派生类中定义,而不是为每个可能的派生类定义。例如,这意味着,如果我有一个表示数学函数的具体对象f,我可以调用f->运算符*g,但如果我想再次调用运算符*以获得对象(f->运算符*g)->运算符*f,我将无法调用,因为函数f*g没有定义为f的*运算符。

我想我应该直接在基类中定义运算符*,但我不知道如何实现运算符,因为我真的不知道如何为产品获得正确的Eval函数,因为我现在不能使用类product,在类function本身中使用从类function派生的类product是没有意义的。我想我也很困惑,写这样的东西是否正确:

 Function* Function::operator*(Function *f) {
 Function *g;
 ...
 //operations that allow g to be have the right Eval function
 //which should of the sort this->Eval(x) * f->Eval(x)
 ...
 return g;
 }

对于如何进行的任何提示或建议,我们将不胜感激。我的水平很基本,我已经编程两个月了。

只是一个草图,你可以做这样的事情:

#include <memory>
// Base Function: f(x) = x
class Function
{
    protected:
    struct Implementation
    {
        virtual ~Implementation() {}
        virtual double evaluate(double x) const { return x; }
    };
    public:
    Function()
    :   self(std::make_shared<Implementation>())
    {}
    double operator () (double x) const { return self->evaluate(x); }
    protected:
    Function(std::shared_ptr<Implementation> self)
    :   self(self)
    {}
    private:
    std::shared_ptr<Implementation> self;
};
typedef Function Identity;

// Unary Function: u(-f(x))
class UnaryMinus : public Function
{
    protected:
    struct Implementation : Function::Implementation
    {
        Function f;
        Implementation(Function f)
        :   f(f)
        {};
        virtual double evaluate(double x) const override { return -f(x); }
    };
    public:
    UnaryMinus(Function f)
    :   Function(std::make_shared<Implementation>(f))
    {}
};
// Binary Function: u(f(x) + g(x))
class BinaryAdd : public Function
{
    protected:
    struct Implementation : Function::Implementation
    {
        Function f;
        Function g;
        Implementation(Function f, Function g)
        :   f(f), g(g)
        {};
        virtual double evaluate(double x) const override { return f(x) + g(x); }
    };
    public:
    BinaryAdd(Function f, Function g)
    :   Function(std::make_shared<Implementation>(f, g))
    {}
};
// Binary Function: u(f(x) * g(x))
class BinaryMultiply : public Function
{
    protected:
    struct Implementation : Function::Implementation
    {
        Function f;
        Function g;
        Implementation(Function f, Function g)
        :   f(f), g(g)
        {};
        virtual double evaluate(double x) const override { return f(x) * g(x); }
    };
    public:
    BinaryMultiply(Function f, Function g)
    :   Function(std::make_shared<Implementation>(f, g))
    {}
};
inline UnaryMinus operator - (Function f) { return UnaryMinus(f); }
inline BinaryAdd operator + (Function f, Function g) { return BinaryAdd(f, g); }
inline BinaryMultiply operator * (Function f, Function g) { return BinaryMultiply(f, g); }
#include <iostream>
int main() {
    Identity x;
    Function result = -x * (x + x) + x;
    std::cout << result(2) << 'n';
}

您可以将operator*重载为独立函数。即使它不是Product的成员,也要将它放在Product.h/cpp中,因为它与CCD_3紧密相关。

Product.h:中

Function* operator*(Function *f, Function *g);

Product.cpp:中

Function* operator*(Function *f, Function *g) {
    return new Product(f, g);
}

与加法、减法等相同

或者,您可以将它们实现为成员函数,但将定义放在Function.cpp中,并在其中包含Product.h等。

请注意您的设计有一个巨大的缺陷。在堆上创建new Function对象,并传递指针。你需要在你的代码中的某个地方delete它们,我想是在你的解构器中。但是,您还需要注意复制对象。通常,手动关心正确的删除是一场噩梦,而且有自动的方法(称为"内存管理")。例如,您可以考虑使用智能指针。看一下std::shared_ptr。虽然不是在所有情况下都最有效,但当你第一次想学习语言时,通常使用它是一件好事,而不是太多关于内存管理的细节。要将shared_ptr应用于代码,请将每个Function*替换为shared_ptr<Function>,并将new Function(...)替换为CCD15(其他类型也是如此)。

还要注意数学函数的*是模糊的:在一些上下文/文献中,f*g的意思是乘以结果,而在另一些上下文/文学中,它的意思是函数卷积。

这里有一个C++11通用编程解决方案,它不依赖于继承的多态性(即虚拟函数),也不需要动态分配。

我不是C++专家,这可能会有很大的改进,但它很有效,并能让人理解这个想法。特别是,下面的代码只适用于double的函数。您可能也可以将操作数和返回类型设置为模板类型,这样就可以在不同的类型上通用(例如,complex)。我不知道确定模板运算符范围的正确方法,这样你就可以使用简写运算符表示法,而不会在其他具有运算符()(双x)的类型上意外调用它们(或使它们变得不明确)。如果有人对这个答案有任何改进的建议,请插话,我会编辑我的答案。

#include <iostream>
using namespace std;
struct Identity
{
  double operator() (double x) const { return x; }
};
struct Constant
{
  template<typename T1>
  Constant(const T1 &x) : _x(x) {}
  double operator()(double x) const { return _x; }
private:
  double _x;
};

template<typename T1>
struct Negate
{
  Negate(const T1 &f) : _f(f) {}
  double operator() (double x) const { return -_f(x); }
private:
  T1 _f;
};
template<typename T1>
struct Reciprocal
{
  Reciprocal(const T1 &f) : _f(f) {}
  double operator() (double x) const { return 1 / _f(x); }
private:
  T1 _f;
};
template<typename T1, typename T2>
struct Sum
{
  Sum(const T1 &f, const T2 &g) : _f(f), _g(g) {}
  double operator() (double x) const { return _f(x) + _g(x); }
private:
  T1 _f;
  T2 _g;
};
template<typename T1, typename T2>
struct Product
{
  Product(const T1 &f, const T2 &g) : _f(f), _g(g) {}
  double operator() (double x) const { return _f(x) * _g(x); }
private:
  T1 _f;
  T2 _g;
};
template<typename T1> 
Negate<T1> operator-(const T1 &f) 
{ return Negate<T1>(f); }
template<typename T1, typename T2> 
Sum<T1, T2> operator+(const T1 &f, const T2 &g) 
{ return Sum<T1, T2>(f, g); }
template<typename T1, typename T2> 
Sum<T1, Negate<T2> > operator-(const T1 &f, const T2 &g) 
{ return Sum<T1, Negate<T2> >(f, Negate<T2>(g)); }
template<typename T1, typename T2> 
Product<T1, T2> operator*(const T1 &f, const T2 &g) 
{ return Product<T1, T2>(f, g); }
template<typename T1, typename T2> 
Product<T1, Reciprocal<T2> > operator/(const T1 &f, const T2 &g) 
{ return Product<T1, Reciprocal<T2> >(f, Reciprocal<T2>(g)); }

int main()
{
  auto f = (Identity() * Constant(4.0) + Constant(5)) / Identity();  // f(x) = (x * 4 + 5) / x; f(2) = 6.5
  auto g = f * f;                                                    // g(x) = f(x) * f(x);     g(2) = 42.25
  cout << f(2) << " " << g(2) << " " << (g / f)(2) << endl;  // prints 6.5 42.25 6.5
  return 0;
}

EDIT:这种方法的主要缺点是"公式"的类型必须在编译时完全已知,并嵌入到模板生成的类中。这意味着非常复杂的公式将生成许多不同的类和代码。因此,这种方法可能会导致严重的代码膨胀。此外,你不能做这样的事情:

for (i = 1; i < j; ++i)  // raise f to the jth power (other than 0)
  f *= f;  

由于f的类型在编译时必须是完全已知的,并且乘法迭代地调用新的类型。其他使用类层次结构、动态分配(带自动清理)和多态性的方法可以做到这一点,并且不会出现代码膨胀的问题。尽管如此,尝试还是很有趣的。