我有以下抽象基类:
class Function {
virtual double Eval(double x) const = 0;
};
我希望能够使用像f * g或f-> operator *(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);}
};
这实际上适用于简单的东西,但问题是operator *仅在基类的单个派生类中定义,而不是为每个可能的派生类定义。这意味着,例如,如果我有一个表示数学函数的具体对象f,我可以调用f-> operator * g但是如果我想再次调用operator *来获取对象(f-> operator * g ) - > operator * f我无法做到,因为函数f * g没有将*运算符定义为f。
我想我应该直接在我的基类中定义operator *,但是我无法弄清楚如何实现运算符,因为我真的不知道如何为产品获得正确的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;
}
任何关于如何进行的提示或建议都表示赞赏。我的水平很基础,我现在已经编程了两个月。
答案 0 :(得分:2)
您可以将operator*
重载为独立功能。即使它不是Product.h/cpp
的成员,也要将它放在Product
中,因为它与它紧密相关。
在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
个函数对象并传递指针。你需要在代码中的某个地方delete
,我假设在你的解构器中。但是你还需要take care about copying your objects。通常,手动关注正确删除是一场噩梦,并且有自动方法(称为"内存管理")。例如,您可以考虑使用智能指针。看看std::shared_ptr
。虽然在每种情况下都不是最有效的,但是当你第一次想要学习语言而不是太多关于内存管理的细节时,通常使用它是一件好事。要将shared_ptr
应用于您的代码,请将每个Function*
替换为shared_ptr<Function>
,并将new Function(...)
替换为make_shared<Function>(...)
(与其他类型相同)。
另请注意,*
数学函数含糊不清:在某些上下文/文献中,f*g
表示将结果乘以,而在其他情境中它意味着功能卷积。
答案 1 :(得分:2)
只是草图,您可能会这样做:
#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';
}
答案 2 :(得分:1)
这是一个C ++ 11通用编程解决方案,它不依赖于继承的多态(即 - 虚函数),也不需要动态分配。
我不是C ++专家,这可能会在很大程度上得到改善,但它可以起作用并获得理念。特别是,下面的代码仅适用于double的函数。您可以将操作数和返回类型设置为模板类型,这样一般可以在不同类型(例如 - 复杂)上工作。我不知道对模板运算符进行范围调整的正确方法,以便您可以使用简写运算符表示法,而不是意外调用(或使其不明确)具有operator()(double 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;
}
编辑:这种方法的主要缺点是“公式”的类型必须在编译时完全已知并嵌入模板生成的类中。这意味着非常复杂的公式将生成许多不同的类和代码。因此,这种方法可能会导致令人讨厌的代码膨胀。此外,你不能做类似的事情:
for (i = 1; i < j; ++i) // raise f to the jth power (other than 0)
f *= f;
由于f的类型必须在编译时完全已知,并且乘法迭代地调用新类型。使用类层次结构,动态分配(使用自动清理)和多态的其他方法可以做到这一点,并且没有代码膨胀的问题。尽管如此,尝试还是很有趣。