我正在研究弗朗西斯和怀特利的C ++科学计算教科书中的练习,并且无法弄清楚如何正确地实施练习7.3。
练习:目标是通过创建一个包含解决常微分方程的方法的抽象类来学习类继承。派生类继承此抽象类,并与用于解决ODE的特定算法相关联,如显式Euler,RK4等。抽象类中的两个方法是纯虚拟,SolveEquation和RightHandSide。练习要求您使用编写派生类ForwardEuler的代码,该类实现显式的Euler方法。
问题:在练习中,他们要求你"导出一个名为FowardEulerSolver的类,允许用户指定函数RightHandSide",但RightHandSide是一个类的成员,我可以'弄清楚如何让用户在主程序中指定一个类方法,这是我认为他们要求的。
问题:有人可以解释C ++中的正确方法,如果有的话,允许用户指定类方法吗?根据书中实现基类的方式,似乎必须有一些方法让用户在主程序中定义RightHandSide函数,然后调用SolveEquation方法来解决与RightHandSide函数关联的ODE
这是本书中给出的抽象类的标题。
class AbstractODESolver
{
public:
AbstractODESolver();
double (*RHS)(double, double);
void SetStepSize(double h);
void SetTimeInterval(double t0, double t1);
void SetInitialValue(double y0);
void setRHS(double (*RHS)(double, double));
double GetStepSize();
double GetInitialTime();
double GetFinalTime();
double GetInitialValue();
virtual double RightHandSide(double y, double t) = 0;
virtual void SolveEquation(std::string filename) = 0;
virtual ~AbstractODESolver();
private:
double stepSize;
double initialTime;
double finalTime;
double initialValue;
};
这是派生类的标题。
class ForwardEulerSolver : public AbstractODESolver
{
public:
ForwardEulerSolver();
double RightHandSide(double y, double t);
void SolveEquation(std::string filename);
virtual ~ForwardEulerSolver();
private:
};
答案 0 :(得分:1)
您可以创建自己的ForwardEulerSolver::RightHandSide
功能。
您不必在main
函数中做任何特殊操作,只需创建ForwardEulerSolver
类的实例并将其指定给指向AbstractODESolver
的指针并调用该函数。编译器将为您处理剩下的工作。
答案 1 :(得分:1)
必须有一些方法让用户在主程序中定义RightHandSide函数,然后调用SolveEquation方法来解决与RightHandSide函数关联的ODE。
对我来说仍然有点不清楚,你究竟要求的是什么,但是从你的评论中你可能希望在抽象类上下文中执行用户定义的函数。我认为这是可能的,但你需要稍微改变一下接口:
class AbstractODESolver
{
public:
AbstractODESolver();
// ...
void SolveEquation(std::string filename) {
// Have an abstract implementation of the algorithm involving
// a call to the RightHandSide() method.
}
virtual ~AbstractODESolver();
protected:
virtual double RightHandSide(double y, double t) = 0;
private:
double stepSize;
double initialTime;
double finalTime;
double initialValue;
};
class ForwardEulerSolver : public AbstractODESolver
{
public:
typedef double (*FnType)(AbstractODESolver*,double,double);
ForwardEulerSolver(FnType fn_) : fn(fn_) { assert(fn); }
virtual ~ForwardEulerSolver();
private:
virtual double RightHandSide(double y, double t)
{
return (*fn)(this,y,t);
}
FnType fn;
};
如果您在c++11环境中工作,可以使用内联lambda函数定义来获取必要的函数指针:
int main()
{
ForwardEulerSolver::FnType rightHandSide =
[](AbstractODESolver* solver, double y, double t) {
// Replace with reasonable user implementation
return 0.0;
};
ForwardEulerSolver fwEulerSolver(rightHandSide);
fwEulerSolver.SolveEquation("MyInput.txt");
}
对于pre c ++ 11标准,您可以使用模块私有函数定义来实现所需行为
namespace {
double RightHandSide(AbstractODESolver* solver, double y, double t) {
// Replace with reasonable user implementation
return 0.0;
}
}
int main()
{
ForwardEulerSolver fwEulerSolver(&RightHandSide);
fwEulerSolver.SolveEquation("MyInput.txt");
}
这或多或少地复制了所谓的Template Method Pattern,但处理了一个特殊的(边缘)用例,允许为算法的特定部分设置用户定义的实现函数。