Я работаю над упражнением Фрэнсиса и Уайтли в учебнике «Научные вычисления на С ++» и не могу понять, как правильно выполнить упражнение 7.3.
Упражнение: Цель состоит в том, чтобы узнать о наследовании классов, создав абстрактный класс, содержащий методы решения обыкновенных дифференциальных уравнений. Производные классы наследуют этот абстрактный класс и связаны с конкретными алгоритмами решения ODE, такими как явный Euler, RK4 и т. Д. Двумя методами в абстрактном классе являются чисто виртуальные, SolveEquation и RightHandSide. В упражнении требуется написать код для производного класса ForwardEuler, который реализует явный метод Эйлера.
Проблема: в этом упражнении они просят вас «Извлечь класс с именем FowardEulerSolver, который позволяет пользователю указывать функцию RightHandSide», но RightHandSide является членом класса, и я не могу понять, как позволить пользователю указать метод класса в основной программе, который, как я предполагаю, они просят.
Вопрос: Может ли кто-нибудь объяснить правильный путь в C ++, если он есть, для того, чтобы пользователь мог указать метод класса? Основываясь на способе реализации базового класса в книге, кажется, что должен быть какой-то способ, чтобы пользователь определил функцию RightHandSide в основной программе, а затем вызвал метод SolveEquation для решения ODE, связанного с этой функцией RightHandSide. ,
Вот заголовок для абстрактного класса, приведенного в книге.
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:
};
должен быть какой-то способ, чтобы пользователь определял функцию RightHandSide в основной программе, а затем вызывал метод SolveEquation для решения ODE, связанного с этой функцией RightHandSide.
Для меня все еще немного неясно, что именно вы просите, но из ваших комментариев кажется, что вы хотите, чтобы пользовательская функция выполнялась в контексте вашего абстрактного класса. Я думаю, что это возможно, но вам нужно немного изменить интерфейсы:
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 В среде вы можете использовать встроенное лямбда-определение функции, чтобы получить необходимый указатель на функцию:
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");
}
Это более или менее воспроизводит то, что называется Шаблон Метод Шаблон, но обрабатывая особый (крайний) вариант использования, чтобы позволить установить пользовательскую функцию реализации для определенной части алгоритма.
Вы делаете свой собственный ForwardEulerSolver::RightHandSide
функция.
Вам не нужно делать ничего особенного в main
функция, просто создайте экземпляр ForwardEulerSolver
класс и назначить ему указатель на AbstractODESolver
и вызвать функцию. Компилятор позаботится об остальном за вас.